home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / unix / rcs.10 < prev    next >
Internet Message Format  |  1989-11-19  |  90KB

  1. Path: xanth!samsung!shadooby!mailrus!cs.utexas.edu!usc!apple!sun-barr!newstop!sun!swap!page
  2. From: page%swap@Sun.COM (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i225:  rcs - revision control system, Part10/14
  5. Message-ID: <128101@sun.Eng.Sun.COM>
  6. Date: 19 Nov 89 09:25:38 GMT
  7. Sender: news@sun.Eng.Sun.COM
  8. Lines: 2967
  9. Approved: page@sun.com
  10.  
  11. Submitted-by: rsbx@cbmvax.commodore.com (Raymond S. Brand)
  12. Posting-number: Volume 89, Issue 225
  13. Archive-name: unix/rcs.10
  14.  
  15. # This is a shell archive.
  16. # Remove anything above and including the cut line.
  17. # Then run the rest of the file through 'sh'.
  18. # Unpacked files will be owned by you and have default permissions.
  19. #----cut here-----cut here-----cut here-----cut here----#
  20. #!/bin/sh
  21. # shar: SHell ARchive
  22. # Run the following text through 'sh' to create:
  23. #    rcs/rcs.rcsfiles/rcsgen.c,v
  24. #    rcs/rcs.rcsfiles/rcskeep.c,v
  25. #    rcs/rcs.rcsfiles/rcskeys.c,v
  26. #    rcs/rcs.rcsfiles/rcslex.c,v
  27. #    rcs/rcs.rcsfiles/rcsrev.c,v
  28. # This is archive 10 of a 14-part kit.
  29. # This archive created: Sun Nov 19 01:12:10 1989
  30. if `test ! -d rcs`
  31. then
  32.   mkdir rcs
  33.   echo "mkdir rcs"
  34. fi
  35. if `test ! -d rcs/rcs.rcsfiles`
  36. then
  37.   mkdir rcs/rcs.rcsfiles
  38.   echo "mkdir rcs/rcs.rcsfiles"
  39. fi
  40. echo "extracting rcs/rcs.rcsfiles/rcsgen.c,v"
  41. sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcsgen.c,v
  42. Xhead     4.7;
  43. Xbranch   4.7.2;
  44. Xaccess   ;
  45. Xsymbols  amiga_rcs:4.7.2 cbmvax_source:4.7.1 uunet_june89_dist:4.7;
  46. Xlocks    ; strict;
  47. Xcomment  @ * @;
  48. X
  49. X
  50. X4.7
  51. Xdate     89.05.01.15.12.49;  author narten;  state Exp;
  52. Xbranches 4.7.1.1 4.7.2.1;
  53. Xnext     ;
  54. X
  55. X4.7.1.1
  56. Xdate     89.08.11.01.42.41;  author rsbx;  state Exp;
  57. Xbranches ;
  58. Xnext     ;
  59. X
  60. X4.7.2.1
  61. Xdate     89.10.13.19.18.42;  author rsbx;  state Exp;
  62. Xbranches ;
  63. Xnext     4.7.2.2;
  64. X
  65. X4.7.2.2
  66. Xdate     89.10.15.15.44.33;  author rsbx;  state Exp;
  67. Xbranches ;
  68. Xnext     4.7.2.3;
  69. X
  70. X4.7.2.3
  71. Xdate     89.11.09.21.29.00;  author rsbx;  state Exp;
  72. Xbranches ;
  73. Xnext     ;
  74. X
  75. X
  76. Xdesc
  77. X@RCS revision generation.
  78. X@
  79. X
  80. X
  81. X
  82. X4.7
  83. Xlog
  84. X@checked in with -k by rsbx at 89.08.10.16.21.17.
  85. X@
  86. Xtext
  87. X@/*
  88. X *                     RCS revision generation
  89. X */
  90. X#ifndef lint
  91. Xstatic char rcsid[]= "$Id: rcsgen.c,v 4.7 89/05/01 15:12:49 narten Exp $ Purdue CS";
  92. X#endif
  93. X
  94. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  95. X * All rights reserved.
  96. X *
  97. X * Redistribution and use in source and binary forms are permitted
  98. X * provided that the above copyright notice and this paragraph are
  99. X * duplicated in all such forms and that any documentation,
  100. X * advertising materials, and other materials related to such
  101. X * distribution and use acknowledge that the software was developed
  102. X * by Walter Tichy.
  103. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  104. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  105. X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  106. X *
  107. X * Report all problems and direct all questions to:
  108. X *   rcs-bugs@@cs.purdue.edu
  109. X * 
  110. X
  111. X
  112. X
  113. X
  114. X
  115. X
  116. X
  117. X*/
  118. X
  119. X
  120. X
  121. X/* $Log:    rcsgen.c,v $
  122. X * Revision 4.7  89/05/01  15:12:49  narten
  123. X * changed copyright header to reflect current distribution rules
  124. X * 
  125. X * Revision 4.6  88/11/08  12:01:13  narten
  126. X * changes from  eggert@@sm.unisys.com (Paul Eggert)
  127. X * 
  128. X * Revision 4.6  88/08/28  14:59:10  eggert
  129. X * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
  130. X * 
  131. X * Revision 4.5  87/12/18  11:43:25  narten
  132. X * additional lint cleanups, and a bug fix from the 4.3BSD version that
  133. X * keeps "ci" from sticking a '\377' into the description if you run it
  134. X * with a zero-length file as the description. (Guy Harris)
  135. X * 
  136. X * Revision 4.4  87/10/18  10:35:10  narten
  137. X * Updating version numbers. Changes relative to 1.1 actually relative to
  138. X * 4.2
  139. X * 
  140. X * Revision 1.3  87/09/24  13:59:51  narten
  141. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  142. X * warnings)
  143. X * 
  144. X * Revision 1.2  87/03/27  14:22:27  jenkins
  145. X * Port to suns
  146. X * 
  147. X * Revision 1.1  84/01/23  14:50:28  kcs
  148. X * Initial revision
  149. X * 
  150. X * Revision 4.2  83/12/02  23:01:39  wft
  151. X * merged 4.1 and 3.3.1.1 (clearerr(stdin)).
  152. X * 
  153. X * Revision 4.1  83/05/10  16:03:33  wft
  154. X * Changed putamin() to abort if trying to reread redirected stdin.
  155. X * Fixed getdesc() to output a prompt on initial newline.
  156. X * 
  157. X * Revision 3.3.1.1  83/10/19  04:21:51  lepreau
  158. X * Added clearerr(stdin) for re-reading description from stdin.
  159. X * 
  160. X * Revision 3.3  82/11/28  21:36:49  wft
  161. X * 4.2 prerelease
  162. X * 
  163. X * Revision 3.3  82/11/28  21:36:49  wft
  164. X * Replaced ferror() followed by fclose() with ffclose().
  165. X * Putdesc() now suppresses the prompts if stdin
  166. X * is not a terminal. A pointer to the current log message is now
  167. X * inserted into the corresponding delta, rather than leaving it in a
  168. X * global variable.
  169. X *
  170. X * Revision 3.2  82/10/18  21:11:26  wft
  171. X * I added checks for write errors during editing, and improved
  172. X * the prompt on putdesc().
  173. X *
  174. X * Revision 3.1  82/10/13  15:55:09  wft
  175. X * corrected type of variables assigned to by getc (char --> int)
  176. X */
  177. X
  178. X
  179. X
  180. X
  181. X#include "rcsbase.h"
  182. X
  183. Xextern struct hshentry * getnum();
  184. Xextern FILE * fopen();
  185. Xextern savestring();
  186. Xextern editstring();
  187. X
  188. Xextern int nextc;          /* next character from lexical analyzer          */
  189. Xextern char Ktext[];       /* keywords from syntax analyzer                 */
  190. Xextern char Klog[];        /* Keyword "log"                                 */
  191. Xextern char Kdesc[];       /* Keyword for description                       */
  192. Xextern FILE * frewrite;    /* new RCS file                                  */
  193. Xextern FILE * fcopy;       /* result file during editing                    */
  194. Xextern char * resultfile;  /* file name for fcopy                           */
  195. Xextern int    rewriteflag; /* indicates whether to rewrite the input file   */
  196. X
  197. X
  198. Xchar    curlogmsg[logsize]; /* buffer for current log message                */
  199. X
  200. Xenum stringwork {copy, edit, expand, edit_expand };
  201. X/* parameter to scandeltatext() */
  202. X
  203. X
  204. X
  205. X
  206. Xchar * buildrevision(deltas, target, dir, expandflag)
  207. Xstruct hshentry ** deltas, * target;
  208. Xchar * dir; int expandflag;
  209. X/* Function: Generates the revision given by target
  210. X * by retrieving all deltas given by parameter deltas and combining them.
  211. X * If dir==nil, the revision is printed on the standard output,
  212. X * otherwise written into a temporary file in directory dir.
  213. X * if expandflag==true, keyword expansion is performed.
  214. X * returns false on errors, the name of the file with the revision otherwise.
  215. X *
  216. X * Algorithm: Copy inital revision unchanged. Then edit all revisions but
  217. X * the last one into it, alternating input and output files (resultfile and
  218. X * editfile). The last revision is then edited in, performing simultaneous
  219. X * keyword substitution (this saves one extra pass).
  220. X * All this simplifies if only one revision needs to be generated,
  221. X * or no keyword expansion is necessary, or if output goes to stdout.
  222. X */
  223. X{
  224. X        int i;
  225. X
  226. X        if (deltas[0]==target) {
  227. X                /* only latest revision to generate */
  228. X                if (dir==nil) {/* print directly to stdout */
  229. X                        fcopy=stdout;
  230. X                        scandeltatext(target,expand);
  231. X                        return(char *) true;
  232. X                } else {
  233. X                        initeditfiles(dir);
  234. X                        scandeltatext(target,expandflag?expand:copy);
  235. X                        ffclose(fcopy);
  236. X                        return(resultfile);
  237. X                }
  238. X        } else {
  239. X                /* several revisions to generate */
  240. X                initeditfiles(dir?dir:"/tmp/");
  241. X                /* write initial revision into fcopy, no keyword expansion */
  242. X                scandeltatext(deltas[0],copy);
  243. X                i = 1;
  244. X                while (deltas[i+1] != nil) {
  245. X                        /* do all deltas except last one */
  246. X                        scandeltatext(deltas[i++],edit);
  247. X                }
  248. X                if (!expandflag) {
  249. X                        /* no keyword expansion; only invoked from ci */
  250. X                        scandeltatext(deltas[i],edit);
  251. X                        finishedit((struct hshentry *)nil);
  252. X                        ffclose(fcopy);
  253. X                } else {
  254. X                        /* perform keyword expansion*/
  255. X                        /* first, get to beginning of file*/
  256. X                        finishedit((struct hshentry *)nil); swapeditfiles(dir==nil);
  257. X                        scandeltatext(deltas[i],edit_expand);
  258. X                        finishedit(deltas[i]);
  259. X                        if (dir!=nil) ffclose(fcopy);
  260. X                }
  261. X                return(resultfile); /*doesn't matter for dir==nil*/
  262. X        }
  263. X}
  264. X
  265. X
  266. X
  267. Xscandeltatext(delta,func)
  268. Xstruct hshentry * delta; enum stringwork func;
  269. X/* Function: Scans delta text nodes up to and including the one given
  270. X * by delta. For the one given by delta, the log message is saved into
  271. X * curlogmsg and the text is processed according to parameter func.
  272. X * Assumes the initial lexeme must be read in first.
  273. X * Does not advance nexttok after it is finished.
  274. X */
  275. X{       struct hshentry * nextdelta;
  276. X
  277. X        do {
  278. X                nextlex();
  279. X                if (!(nextdelta=getnum())) {
  280. X                        fatserror("Can't find delta for revision %s", delta->num);
  281. X                }
  282. X                if (!getkey(Klog) || nexttok!=STRING)
  283. X                        serror("Missing log entry");
  284. X                elsif (delta==nextdelta) {
  285. X                        VOID savestring(curlogmsg,logsize);
  286. X                        delta->log=curlogmsg;
  287. X                } else {readstring();
  288. X                        delta->log= "";
  289. X                }
  290. X                nextlex();
  291. X                if (!getkey(Ktext) || nexttok!=STRING)
  292. X                        fatserror("Missing delta text");
  293. X
  294. X                if(delta==nextdelta)
  295. X                        /* got the one we're looking for */
  296. X                        switch (func) {
  297. X                        case copy:      copystring();
  298. X                                        break;
  299. X                        case expand:    xpandstring(delta);
  300. X                                        break;
  301. X                        case edit:      editstring((struct hshentry *)nil);
  302. X                                        break;
  303. X                        case edit_expand: editstring(delta);
  304. X                                        break;
  305. X                        }
  306. X                else    readstring(); /* skip over it */
  307. X
  308. X        } while (delta!=nextdelta);
  309. X}
  310. X
  311. X
  312. Xint stdinread; /* stdinread>0 if redirected stdin has been read once */
  313. X
  314. Xint ttystdin()
  315. X{
  316. X    static int initialized, istty;
  317. X    if (!initialized) {
  318. X        istty = isatty(fileno(stdin));
  319. X        initialized = 1;
  320. X    }
  321. X    return istty;
  322. X}
  323. X
  324. Xputdesc(initflag,textflag,textfile,quietflag)
  325. Xint initflag,textflag; char * textfile; int quietflag;
  326. X/* Function: puts the descriptive text into file frewrite.
  327. X * if !initflag && !textflag, the text is copied from the old description.
  328. X * Otherwise, if the textfile!=nil, the text is read from that
  329. X * file, or from stdin, if textfile==nil.
  330. X * Increments stdinread if text is read from redirected stdin.
  331. X * if initflag&&quietflag&&!textflag, an empty text is inserted.
  332. X * if !initflag, the old descriptive text is discarded.
  333. X */
  334. X{       register FILE * txt; register int c, old1, old2;
  335. X    register FILE * frew;
  336. X#ifdef lint
  337. X    if (quietflag ==  0) initflag = quietflag; /* silencelint */
  338. X#endif
  339. X
  340. X    frew = frewrite;
  341. X        if (!initflag && !textflag) {
  342. X                /* copy old description */
  343. X                VOID fprintf(frew,"\n\n%s%c",Kdesc,nextc);
  344. X                rewriteflag=true; getdesc(false);
  345. X        } else {
  346. X                /* get new description */
  347. X               if (!initflag) {
  348. X                        /*skip old description*/
  349. X                        rewriteflag=false; getdesc(false);
  350. X                }
  351. X                VOID fprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
  352. X                if (textfile) {
  353. X                        old1='\n';
  354. X                        /* copy textfile */
  355. X                        if ((txt=fopen(textfile,"r"))!=NULL) {
  356. X                                while ((c=getc(txt))!=EOF) {
  357. X                                        if (c==SDELIM) VOID putc(c,frew); /*double up*/
  358. X                                        VOID putc(c,frew);
  359. X                                        old1=c;
  360. X                                }
  361. X                                if (old1!='\n') VOID putc('\n',frew);
  362. X                                VOID fclose(txt);
  363. X                VOID putc(SDELIM,frew);
  364. X                VOID fputs("\n\n", frew);
  365. X                return;
  366. X                        } else {
  367. X                                error("Can't open file %s with description",textfile);
  368. X                                if (!ttystdin()) return;
  369. X                                /* otherwise, get description from terminal */
  370. X                        }
  371. X                }
  372. X                /* read text from stdin */
  373. X                if (ttystdin()) {
  374. X                    VOID fputs("enter description, terminated with ^D or '.':\n",stderr);
  375. X                    VOID fputs("NOTE: This is NOT the log message!\n>> ",stderr);
  376. X            if (feof(stdin))
  377. X                    clearerr(stdin);
  378. X                } else {  /* redirected stdin */
  379. X                    if (stdinread>0)
  380. X                        faterror("Can't reread redirected stdin for description; use -t<file>");
  381. X                    stdinread++;
  382. X                }
  383. X                c = '\0'; old2= '\n';
  384. X                if ((old1=getchar())==EOF) {
  385. X                        if (ttystdin()) {
  386. X                             VOID putc('\n',stderr);
  387. X                             clearerr(stdin);
  388. X            }
  389. X        } else {
  390. X             if (old1=='\n' && ttystdin())
  391. X             VOID fputs(">> ",stderr);
  392. X             for (;;) {
  393. X                            c=getchar();
  394. X                            if (c==EOF) {
  395. X                                    if (ttystdin()) {
  396. X                                            VOID putc('\n',stderr);
  397. X                                            clearerr(stdin);
  398. X                    }
  399. X                                    VOID putc(old1,frew);
  400. X                                    if (old1!='\n') VOID putc('\n',frew);
  401. X                                    break;
  402. X                            }
  403. X                            if (c=='\n' && old1=='.' && old2=='\n') {
  404. X                                    break;
  405. X                            }
  406. X                            if (c=='\n' && ttystdin()) VOID fputs(">> ",stderr);
  407. X                if(old1==SDELIM) VOID putc(old1,frew); /* double up*/
  408. X                VOID putc(old1,frew);
  409. X                            old2=old1;
  410. X                            old1=c;
  411. X                    } /* end for */
  412. X        }
  413. X        VOID putc(SDELIM,frew); VOID fputs("\n\n",frew);
  414. X        }
  415. X}
  416. X@
  417. X
  418. X
  419. X4.7.2.1
  420. Xlog
  421. X@Start of Amiga RCS port branch.
  422. X@
  423. Xtext
  424. X@d5 1
  425. Xa5 5
  426. X<<<<<<< rcsgen.c
  427. Xstatic char rcsid[]= "$Id: rcsgen.c,v 4.7.1.1 89/08/11 01:42:41 rsbx Exp Locker: rsbx $ Purdue CS";
  428. X=======
  429. Xstatic char rcsid[]= "$Id: rcsgen.c,v 1.2 89/09/17 13:35:51 rick Exp $ Purdue CS";
  430. X>>>>>>> 1.2
  431. Xa35 11
  432. X<<<<<<< rcsgen.c
  433. X * Revision 4.7.1.1  89/08/11  01:42:41  rsbx
  434. X * Start of cbmvax RCS source branch.
  435. X=======
  436. X * Revision 1.2  89/09/17  13:35:51  rick
  437. X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
  438. X * All changes done with conditional compile (#ifdef AMIGA).  This version
  439. X * compiles correctly with Lattice C version 5.02 or later.
  440. X>>>>>>> 1.2
  441. X * 
  442. X<<<<<<< rcsgen.c
  443. Xa36 3
  444. X * checked in with -k by rsbx at 89.08.10.16.21.17.
  445. X * 
  446. X * Revision 4.7  89/05/01  15:12:49  narten
  447. Xa44 8
  448. X=======
  449. X * Revision 1.3  89/09/16  09:43:28  rick
  450. X * Modified AMIGA changes to work with Lattice C
  451. X * 
  452. X * Revision 1.2  88/09/03  15:10:56  rick
  453. X * Port to AmigaDos.  All done with conditional compiles
  454. X * 
  455. X>>>>>>> 1.2
  456. Xa111 1
  457. X<<<<<<< rcsgen.c
  458. Xa112 4
  459. X=======
  460. Xchar    curlogmsg[logsize] /* buffer for current log message                */
  461. X        ="";
  462. X>>>>>>> 1.2
  463. Xa153 3
  464. X#ifdef AMIGA
  465. X                initeditfiles(dir?dir:"t:");
  466. X#else
  467. Xa154 1
  468. X#endif
  469. Xa289 3
  470. X#ifdef AMIGA
  471. X            fflush(stderr);
  472. X#endif
  473. Xa303 1
  474. X<<<<<<< rcsgen.c
  475. Xa304 3
  476. X=======
  477. X             if (old1=='\n' && isatty(fileno(stdin))) {
  478. X>>>>>>> 1.2
  479. Xa305 4
  480. X#ifdef AMIGA
  481. X             fflush(stderr);
  482. X#endif
  483. X             }
  484. Xa319 1
  485. X<<<<<<< rcsgen.c
  486. Xa322 10
  487. X=======
  488. X                            if (c=='\n' && isatty(fileno(stdin))) {
  489. X                                VOID fputs(">> ",stderr);
  490. X#ifdef AMIGA
  491. X                fflush(stderr);
  492. X#endif
  493. X                }
  494. X                            if(old1==SDELIM) VOID putc(old1,frewrite); /* double up*/
  495. X                            VOID putc(old1,frewrite);
  496. X>>>>>>> 1.2
  497. X@
  498. X
  499. X
  500. X4.7.2.2
  501. Xlog
  502. X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  503. Xsources I have here (and are later than the ones Rick used).
  504. X@
  505. Xtext
  506. X@d5 5
  507. Xa9 1
  508. Xstatic char rcsid[]= "$Id: rcsgen.c,v 4.7.2.1 89/10/13 19:18:42 rsbx Exp Locker: rsbx $ Purdue CS";
  509. Xd40 1
  510. Xa40 3
  511. X * Revision 4.7.2.1  89/10/13  19:18:42  rsbx
  512. X * Start of Amiga RCS port branch.
  513. X * 
  514. Xd43 6
  515. Xd50 1
  516. Xd63 8
  517. Xd138 3
  518. Xd143 1
  519. Xd335 1
  520. Xa335 1
  521. X                old2= '\n';
  522. Xd342 3
  523. Xd346 1
  524. Xd366 5
  525. Xd379 1
  526. X@
  527. X
  528. X
  529. X4.7.2.3
  530. Xlog
  531. X@Changed "^D" to "^\".
  532. X@
  533. Xtext
  534. X@d5 1
  535. Xa5 1
  536. Xstatic char rcsid[]= "$Id: rcsgen.c,v 4.7.2.2 89/10/15 15:44:33 rsbx Exp Locker: rsbx $ Purdue CS";
  537. Xa35 4
  538. X * Revision 4.7.2.2  89/10/15  15:44:33  rsbx
  539. X * Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  540. X * sources I have here (and are later than the ones Rick used).
  541. X * 
  542. Xa301 3
  543. X#ifdef AMIGA
  544. X                    VOID fputs("enter description, terminated with ^\\ or '.':\n",stderr);
  545. X#else
  546. Xa302 1
  547. X#endif
  548. X@
  549. X
  550. X
  551. X4.7.1.1
  552. Xlog
  553. X@Start of cbmvax RCS source branch.
  554. X@
  555. Xtext
  556. X@a36 3
  557. X * checked in with -k by rsbx at 89.08.10.16.21.17.
  558. X * 
  559. X * Revision 4.7  89/05/01  15:12:49  narten
  560. X@
  561. SHAR_EOF
  562. echo "extracting rcs/rcs.rcsfiles/rcskeep.c,v"
  563. sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcskeep.c,v
  564. Xhead     4.6;
  565. Xbranch   4.6.2;
  566. Xaccess   ;
  567. Xsymbols  amiga_rcs:4.6.2 cbmvax_source:4.6.1 uunet_june89_dist:4.6;
  568. Xlocks    ; strict;
  569. Xcomment  @ * @;
  570. X
  571. X
  572. X4.6
  573. Xdate     89.05.01.15.12.56;  author narten;  state Exp;
  574. Xbranches 4.6.1.1 4.6.2.1;
  575. Xnext     ;
  576. X
  577. X4.6.1.1
  578. Xdate     89.08.11.01.42.46;  author rsbx;  state Exp;
  579. Xbranches ;
  580. Xnext     ;
  581. X
  582. X4.6.2.1
  583. Xdate     89.10.13.19.18.47;  author rsbx;  state Exp;
  584. Xbranches ;
  585. Xnext     4.6.2.2;
  586. X
  587. X4.6.2.2
  588. Xdate     89.10.15.15.44.38;  author rsbx;  state Exp;
  589. Xbranches ;
  590. Xnext     ;
  591. X
  592. X
  593. Xdesc
  594. X@RCS keyword extraction.
  595. X@
  596. X
  597. X
  598. X
  599. X4.6
  600. Xlog
  601. X@checked in with -k by rsbx at 89.08.10.16.21.35.
  602. X@
  603. Xtext
  604. X@/*
  605. X *                     RCS keyword extraction
  606. X */
  607. X#ifndef lint
  608. Xstatic char rcsid[]= "$Id: rcskeep.c,v 4.6 89/05/01 15:12:56 narten Exp $ Purdue CS";
  609. X#endif
  610. X/*****************************************************************************
  611. X *                       main routine: getoldkeys()
  612. X *                       Testprogram: define KEEPTEST
  613. X *****************************************************************************
  614. X */
  615. X
  616. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  617. X * All rights reserved.
  618. X *
  619. X * Redistribution and use in source and binary forms are permitted
  620. X * provided that the above copyright notice and this paragraph are
  621. X * duplicated in all such forms and that any documentation,
  622. X * advertising materials, and other materials related to such
  623. X * distribution and use acknowledge that the software was developed
  624. X * by Walter Tichy.
  625. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  626. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  627. X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  628. X *
  629. X * Report all problems and direct all questions to:
  630. X *   rcs-bugs@@cs.purdue.edu
  631. X * 
  632. X
  633. X
  634. X
  635. X
  636. X
  637. X
  638. X
  639. X*/
  640. X
  641. X
  642. X
  643. X/* $Log:    rcskeep.c,v $
  644. X * Revision 4.6  89/05/01  15:12:56  narten
  645. X * changed copyright header to reflect current distribution rules
  646. X * 
  647. X * Revision 4.5  88/11/08  12:01:05  narten
  648. X * changes from  eggert@@sm.unisys.com (Paul Eggert)
  649. X * 
  650. X * Revision 4.5  88/08/09  19:13:03  eggert
  651. X * Remove lint and speed up by making FILE *fp local, not global.
  652. X * 
  653. X * Revision 4.4  87/12/18  11:44:21  narten
  654. X * more lint cleanups (Guy Harris)
  655. X * 
  656. X * Revision 4.3  87/10/18  10:35:50  narten
  657. X * Updating version numbers. Changes relative to 1.1 actually relative
  658. X * to 4.1
  659. X * 
  660. X * Revision 1.3  87/09/24  14:00:00  narten
  661. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  662. X * warnings)
  663. X * 
  664. X * Revision 1.2  87/03/27  14:22:29  jenkins
  665. X * Port to suns
  666. X * 
  667. X * Revision 1.1  84/01/23  14:50:30  kcs
  668. X * Initial revision
  669. X * 
  670. X * Revision 4.1  83/05/10  16:26:44  wft
  671. X * Added new markers Id and RCSfile; extraction added.
  672. X * Marker matching with trymatch().
  673. X * 
  674. X * Revision 3.2  82/12/24  12:08:26  wft
  675. X * added missing #endif.
  676. X *
  677. X * Revision 3.1  82/12/04  13:22:41  wft
  678. X * Initial revision.
  679. X *
  680. X */
  681. X
  682. X/*
  683. X#define KEEPTEST
  684. X/* Testprogram; prints out the keyword values found. */
  685. X
  686. X#include  "rcsbase.h"
  687. Xextern char * checkid();
  688. Xextern FILE * fopen();
  689. Xstatic int getval();
  690. Xextern enum markers trymatch();
  691. X
  692. X#define IDLENGTH 30
  693. Xchar prevauthor[IDLENGTH];
  694. Xchar prevdate[datelength];
  695. Xchar prevRCS[NCPFN];
  696. Xchar prevrev[revlength];
  697. Xchar prevsource[NCPPN];
  698. Xchar prevstate [IDLENGTH];
  699. Xchar prevlocker[IDLENGTH];
  700. Xchar dummy[IDLENGTH];
  701. X
  702. Xgetoldkeys(fname)
  703. Xchar * fname;
  704. X/* Function: Tries to read keyword values for author, date,
  705. X * revision number, RCS file, (both with and without path),
  706. X * state, and workfilename out of the file fname.
  707. X * The results are placed into
  708. X * prevauthor, prevdate, prevRCS, prevrev, prevsource, prevstate.
  709. X * Aborts immediately if it finds an error and returns false.
  710. X * If it returns true, it doesn't mean that any of the
  711. X * values were found; instead, check to see whether the corresponding arrays
  712. X * contain the empty string.
  713. X */
  714. X{
  715. X    register FILE *fp;
  716. X    register int c;
  717. X    char keyword[keylength+2];
  718. X    register char * tp;
  719. X    enum markers mresult;
  720. X
  721. X    /* initialize to empty */
  722. X    prevauthor[0]=prevsource[0]=prevstate[0]=prevdate[0]=prevrev[0]= '\0';
  723. X
  724. X    if ( (fp = fopen(fname, "r") ) == NULL ) {
  725. X       error("Can't open %s\n", fname);
  726. X       return false;
  727. X    }
  728. X    while( (c=getc(fp)) != EOF) {
  729. X        if ( c==KDELIM) {
  730. X            /* try to get keyword */
  731. X            tp = keyword;
  732. X        while( (c=getc(fp))!=EOF && (tp< keyword+keylength) && (c!='\n')
  733. X           && (c!=KDELIM) && (c!=VDELIM))
  734. X          *tp++ = c;
  735. X
  736. X            if (c==KDELIM) {VOID ungetc(c,fp);continue;}
  737. X            if (c!=VDELIM) continue;
  738. X        *tp++ = c;
  739. X            *tp='\0';
  740. X            while ((c=getc(fp))==' '||c=='\t'); /* skip blanks */
  741. X            VOID ungetc(c,fp); /* needed for getval */
  742. X
  743. X        switch (mresult=trymatch(keyword,true)) {
  744. X            case Author:
  745. X        if (getval(fp,prevauthor,IDLENGTH,true))
  746. X                    if (!checkid(prevauthor, '\0')) goto errexit;
  747. X                break;
  748. X            case Date:
  749. X        if (!getprevdate(fp,true)) goto errexit;
  750. X                break;
  751. X            case Header:
  752. X            case Id:
  753. X        if (mresult==Header) {
  754. X            if (!getval(fp,prevsource,NCPPN,true)) break; /*unexpanded*/
  755. X        } else {
  756. X            if (!getval(fp,prevRCS,NCPFN,true))    break; /*unexpanded*/
  757. X        }
  758. X        if (!getval(fp,prevrev,revlength,false)) goto errexit;
  759. X        if (!checknum(prevrev,-1)) {
  760. X            error("Bad revision number");
  761. X            goto errexit;
  762. X        }
  763. X        if (!getprevdate(fp,false)) goto errexit;
  764. X        if (!getval(fp,prevauthor,IDLENGTH,false)) goto errexit;
  765. X        if (!checkid(prevauthor, '\0')) goto errexit;
  766. X        if (!getval(fp,prevstate,IDLENGTH,false)) goto errexit;
  767. X        if (!checkid(prevstate, '\0')) goto errexit;
  768. X        VOID getval(fp, dummy, IDLENGTH, true);    /* optional locker*/
  769. X        VOID getval(fp, prevlocker,IDLENGTH,true); /* optional locker*/
  770. X                break;
  771. X            case Locker:
  772. X                VOID getval(fp,prevlocker,IDLENGTH,true);
  773. X        if (!checkid(prevlocker, '\0')) goto errexit;
  774. X                break;
  775. X            case Log:
  776. X        VOID getval(fp,prevRCS,NCPPN,true);
  777. X                break;
  778. X            case RCSfile:
  779. X                VOID getval(fp,prevRCS,NCPFN,true);
  780. X                break;
  781. X            case Revision:
  782. X                if (getval(fp,prevrev,revlength,true))
  783. X                    if (!checknum(prevrev,-1)) {
  784. X                        error("Bad revision number");
  785. X                        goto errexit;
  786. X                    }
  787. X                break;
  788. X            case Source:
  789. X                VOID getval(fp,prevsource,NCPPN,true);
  790. X                break;
  791. X            case State:
  792. X                if (getval(fp,prevstate,IDLENGTH,true))
  793. X                    if (!checkid(prevstate, '\0')) goto errexit;
  794. X                break;
  795. X            default:
  796. X               continue;
  797. X            }
  798. X            if (getc(fp)!=KDELIM)
  799. X                warn("Closing %c missing on keyword",KDELIM);
  800. X            if (prevauthor[0]!='\0'&&prevrev[0]!='\0'&&prevstate[0]!='\0'&&
  801. X                prevdate[0]!='\0' &&
  802. X         ((prevsource[0]!='\0')||(prevRCS[0]!='\0'))){
  803. X                /* done; prevlocker is irrelevant */
  804. X                break;
  805. X           }
  806. X        }
  807. X    }
  808. X    VOID fclose(fp);
  809. X    return true;
  810. X
  811. Xerrexit:
  812. X    prevauthor[0]=prevsource[0]=prevstate[0]=prevdate[0]=prevrev[0]= '\0';
  813. X    VOID fclose(fp); return false;
  814. X}
  815. X
  816. X
  817. Xstatic int getval(fp,target,maxchars,optional)
  818. Xregister FILE *fp;
  819. Xchar * target; int maxchars, optional;
  820. X/* Function: Places a keyword value into target, but not more
  821. X * than maxchars characters. Prints an error if optional==false
  822. X * and there is no keyword. Returns true if one is found, false otherwise.
  823. X */
  824. X{   register char * tp;
  825. X    register int c;
  826. X
  827. X    tp=target;
  828. X    c=getc(fp);
  829. X    if (c==KDELIM) {
  830. X        if (!optional)
  831. X            error("Missing keyword value");
  832. X        VOID ungetc(c,fp);
  833. X        return false;
  834. X    } else {
  835. X        while (!(c==' '||c=='\n'||c=='\t'||c==KDELIM||c==EOF)) {
  836. X            if (tp-target>=maxchars-1) {
  837. X                error("keyword value too long");
  838. X                return false;
  839. X            } else {
  840. X                *tp++ =c;
  841. X                c=getc(fp);
  842. X            }
  843. X        }
  844. X        *tp= '\0';
  845. X#       ifdef KEEPTEST
  846. X        VOID printf("getval: %s\n",target);
  847. X#       endif
  848. X        while(c==' '||c=='\t') c=getc(fp); /* skip trailing blanks */
  849. X    }
  850. X    VOID ungetc(c,fp);
  851. X    return true;
  852. X}
  853. X
  854. X
  855. Xint getprevdate(fp,optional)
  856. XFILE *fp;
  857. Xint optional;
  858. X/* Function: reads a date prevdate; checks format
  859. X * If there is not date and optional==false, an error is printed.
  860. X * Returns false on error, true otherwise.
  861. X */
  862. X{   char prevday[10];
  863. X    char prevtime[10];
  864. X
  865. X    prevday[0]=prevtime[0]='\0';
  866. X    if (!getval(fp,prevday,9,optional)) return optional;
  867. X    if (!getval(fp,prevtime,9,false)) return false;
  868. X    /*process date */
  869. X    prevday[2]=prevday[5]=prevday[8]=prevtime[2]=prevtime[5]='.';
  870. X    prevday[9]='\0';
  871. X    VOID strcpy(prevdate,prevday);
  872. X    VOID strcat(prevdate,prevtime);
  873. X    if (!checknum(prevdate,5)) {
  874. X            error("Bad date: %s",prevdate);
  875. X            prevdate[0]='\0';
  876. X            return false;
  877. X    }
  878. X    return true;
  879. X}
  880. X
  881. Xint checknum(sp,fields)
  882. Xregister char * sp; int fields;
  883. X{    register int dotcount;
  884. X     if (sp==nil||*sp=='\0') return true;
  885. X     dotcount=0;
  886. X     while(*sp) {
  887. X        if (*sp=='.') dotcount++;
  888. X        elsif (ctab[*sp]!=DIGIT) return false;
  889. X        sp++;
  890. X     }
  891. X     if (fields >= 0 && dotcount!=fields) return false;
  892. X     return true;
  893. X}
  894. X
  895. X
  896. X
  897. X#ifdef KEEPTEST
  898. Xchar * RCSfilename, * workfilename;
  899. X
  900. Xmain(argc, argv)
  901. Xint  argc; char  *argv[];
  902. X{
  903. X    cmdid="keeptest";
  904. X        while (*(++argv)) {
  905. X                if (getoldkeys(*argv))
  906. X                VOID printf("%s:  revision: %s, date: %s, author: %s, state: %s\n",
  907. X                        *argv, prevrev, prevdate, prevauthor,prevstate);
  908. X        VOID printf("Source: %s, RCSfile: %s\n",prevsource,prevRCS);
  909. X    }
  910. X    exit(0);
  911. X}
  912. X#endif
  913. X@
  914. X
  915. X
  916. X4.6.2.1
  917. Xlog
  918. X@Start of Amiga RCS port branch.
  919. X@
  920. Xtext
  921. X@d5 1
  922. Xa5 5
  923. X<<<<<<< rcskeep.c
  924. Xstatic char rcsid[]= "$Id: rcskeep.c,v 4.6.1.1 89/08/11 01:42:46 rsbx Exp Locker: rsbx $ Purdue CS";
  925. X=======
  926. Xstatic char rcsid[]= "$Id: rcskeep.c,v 1.2 89/09/17 13:35:58 rick Exp $ Purdue CS";
  927. X>>>>>>> 1.2
  928. Xa40 16
  929. X<<<<<<< rcskeep.c
  930. X * Revision 4.6.1.1  89/08/11  01:42:46  rsbx
  931. X * Start of cbmvax RCS source branch.
  932. X * 
  933. X * Revision 4.6  89/05/01  15:12:56  narten
  934. X * checked in with -k by rsbx at 89.08.10.16.21.35.
  935. X=======
  936. X * Revision 1.2  89/09/17  13:35:58  rick
  937. X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
  938. X * All changes done with conditional compile (#ifdef AMIGA).  This version
  939. X * compiles correctly with Lattice C version 5.02 or later.
  940. X * 
  941. X * Revision 1.2  88/09/03  15:11:14  rick
  942. X * Port to AmigaDos.  All done with conditional compiles
  943. X>>>>>>> 1.2
  944. X * 
  945. X@
  946. X
  947. X
  948. X4.6.2.2
  949. Xlog
  950. X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  951. Xsources I have here (and are later than the ones Rick used).
  952. X@
  953. Xtext
  954. X@d5 5
  955. Xa9 1
  956. Xstatic char rcsid[]= "$Id: rcskeep.c,v 4.6.2.1 89/10/13 19:18:47 rsbx Exp Locker: rsbx $ Purdue CS";
  957. Xd45 1
  958. Xa45 3
  959. X * Revision 4.6.2.1  89/10/13  19:18:47  rsbx
  960. X * Start of Amiga RCS port branch.
  961. X * 
  962. Xd51 9
  963. X@
  964. X
  965. X
  966. X4.6.1.1
  967. Xlog
  968. X@Start of cbmvax RCS source branch.
  969. X@
  970. Xtext
  971. X@a41 3
  972. X * checked in with -k by rsbx at 89.08.10.16.21.35.
  973. X * 
  974. X * Revision 4.6  89/05/01  15:12:56  narten
  975. X@
  976. SHAR_EOF
  977. echo "extracting rcs/rcs.rcsfiles/rcskeys.c,v"
  978. sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcskeys.c,v
  979. Xhead     4.3;
  980. Xbranch   4.3.2;
  981. Xaccess   ;
  982. Xsymbols  amiga_rcs:4.3.2 cbmvax_source:4.3.1 uunet_june89_dist:4.3;
  983. Xlocks    ; strict;
  984. Xcomment  @ * @;
  985. X
  986. X
  987. X4.3
  988. Xdate     89.05.01.15.13.02;  author narten;  state Exp;
  989. Xbranches 4.3.1.1 4.3.2.1;
  990. Xnext     ;
  991. X
  992. X4.3.1.1
  993. Xdate     89.08.11.01.42.49;  author rsbx;  state Exp;
  994. Xbranches ;
  995. Xnext     ;
  996. X
  997. X4.3.2.1
  998. Xdate     89.10.13.19.18.51;  author rsbx;  state Exp;
  999. Xbranches ;
  1000. Xnext     4.3.2.2;
  1001. X
  1002. X4.3.2.2
  1003. Xdate     89.10.15.15.44.42;  author rsbx;  state Exp;
  1004. Xbranches ;
  1005. Xnext     ;
  1006. X
  1007. X
  1008. Xdesc
  1009. X@RCS keyword table and match operation.
  1010. X@
  1011. X
  1012. X
  1013. X
  1014. X4.3
  1015. Xlog
  1016. X@checked in with -k by rsbx at 89.08.10.16.22.01.
  1017. X@
  1018. Xtext
  1019. X@/*
  1020. X *                     RCS keyword table and match operation
  1021. X */
  1022. X#ifndef lint
  1023. Xstatic char rcsid[]= "$Id: rcskeys.c,v 4.3 89/05/01 15:13:02 narten Exp $ Purdue CS";
  1024. X#endif
  1025. X
  1026. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  1027. X * All rights reserved.
  1028. X *
  1029. X * Redistribution and use in source and binary forms are permitted
  1030. X * provided that the above copyright notice and this paragraph are
  1031. X * duplicated in all such forms and that any documentation,
  1032. X * advertising materials, and other materials related to such
  1033. X * distribution and use acknowledge that the software was developed
  1034. X * by Walter Tichy.
  1035. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  1036. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  1037. X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  1038. X *
  1039. X * Report all problems and direct all questions to:
  1040. X *   rcs-bugs@@cs.purdue.edu
  1041. X * 
  1042. X
  1043. X
  1044. X
  1045. X
  1046. X
  1047. X
  1048. X
  1049. X*/
  1050. X
  1051. X
  1052. X
  1053. X/* $Log:    rcskeys.c,v $
  1054. X * Revision 4.3  89/05/01  15:13:02  narten
  1055. X * changed copyright header to reflect current distribution rules
  1056. X * 
  1057. X * Revision 4.2  87/10/18  10:36:33  narten
  1058. X * Updating version numbers. Changes relative to 1.1 actuallyt
  1059. X * relative to 4.1
  1060. X * 
  1061. X * Revision 1.2  87/09/24  14:00:10  narten
  1062. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  1063. X * warnings)
  1064. X * 
  1065. X * Revision 1.1  84/01/23  14:50:32  kcs
  1066. X * Initial revision
  1067. X * 
  1068. X * Revision 4.1  83/05/04  10:06:53  wft
  1069. X * Initial revision.
  1070. X * 
  1071. X */
  1072. X
  1073. X
  1074. X#include "rcsbase.h"
  1075. X
  1076. X
  1077. X
  1078. Xstruct { char * keyword; enum markers marker;} markertable[] =
  1079. X        {{AUTHOR,   Author  },
  1080. X         {DATE,     Date    },
  1081. X         {HEADER,   Header  },
  1082. X         {IDH,      Id      },
  1083. X         {LOCKER,   Locker  },
  1084. X         {LOG,      Log     },
  1085. X         {RCSFILE,  RCSfile },
  1086. X         {REVISION, Revision},
  1087. X         {SOURCE,   Source  },
  1088. X         {STATE,    State   },
  1089. X         {nil,      Nomatch }};
  1090. X
  1091. X
  1092. X
  1093. Xenum markers trymatch(string,onlyvdelim)
  1094. Xchar * string;
  1095. X/* function: Checks whether string starts with a keyword followed
  1096. X * by a KDELIM or a VDELIM. If onlyvdelim==true, only a VDELIM
  1097. X * may follow the keyword.
  1098. X * If successful, returns the appropriate marker, otherwise Nomatch.
  1099. X */
  1100. X{
  1101. X        register int j;
  1102. X    register char * p, * s;
  1103. X        for (j=0; markertable[j].keyword!=nil; j++ ) {
  1104. X        /* try next keyword */
  1105. X        p = markertable[j].keyword; s = string;
  1106. X        while (*p!='\0' && *s!='\0' && *p == *s) {
  1107. X            p++; s++;
  1108. X        }
  1109. X        if (*p != '\0') continue; /* no match */
  1110. X        if ((*s == VDELIM) || (!onlyvdelim && (*s == KDELIM)))
  1111. X            return(markertable[j].marker);
  1112. X        }
  1113. X        return(Nomatch);
  1114. X}
  1115. X
  1116. X@
  1117. X
  1118. X
  1119. X4.3.2.1
  1120. Xlog
  1121. X@Start of Amiga RCS port branch.
  1122. X@
  1123. Xtext
  1124. X@d5 1
  1125. Xa5 5
  1126. X<<<<<<< rcskeys.c
  1127. Xstatic char rcsid[]= "$Id: rcskeys.c,v 4.3.1.1 89/08/11 01:42:49 rsbx Exp Locker: rsbx $ Purdue CS";
  1128. X=======
  1129. Xstatic char rcsid[]= "$Id: rcskeys.c,v 1.2 89/09/17 13:36:05 rick Exp $ Purdue CS";
  1130. X>>>>>>> 1.2
  1131. Xa35 4
  1132. X<<<<<<< rcskeys.c
  1133. X * Revision 4.3.1.1  89/08/11  01:42:49  rsbx
  1134. X * Start of cbmvax RCS source branch.
  1135. X * 
  1136. Xa36 3
  1137. X * checked in with -k by rsbx at 89.08.10.16.22.01.
  1138. X * 
  1139. X * Revision 4.3  89/05/01  15:13:02  narten
  1140. Xa37 9
  1141. X=======
  1142. X * Revision 1.2  89/09/17  13:36:05  rick
  1143. X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
  1144. X * All changes done with conditional compile (#ifdef AMIGA).  This version
  1145. X * compiles correctly with Lattice C version 5.02 or later.
  1146. X * 
  1147. X * Revision 1.2  88/09/03  15:11:32  rick
  1148. X * Port to AmigaDos.  All done with conditional compiles
  1149. X>>>>>>> 1.2
  1150. X@
  1151. X
  1152. X
  1153. X4.3.2.2
  1154. Xlog
  1155. X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  1156. Xsources I have here (and are later than the ones Rick used).
  1157. X@
  1158. Xtext
  1159. X@d5 5
  1160. Xa9 1
  1161. Xstatic char rcsid[]= "$Id: rcskeys.c,v 4.3.2.1 89/10/13 19:18:51 rsbx Exp Locker: rsbx $ Purdue CS";
  1162. Xd40 1
  1163. Xa40 3
  1164. X * Revision 4.3.2.1  89/10/13  19:18:51  rsbx
  1165. X * Start of Amiga RCS port branch.
  1166. X * 
  1167. Xd49 9
  1168. X@
  1169. X
  1170. X
  1171. X4.3.1.1
  1172. Xlog
  1173. X@Start of cbmvax RCS source branch.
  1174. X@
  1175. Xtext
  1176. X@a36 3
  1177. X * checked in with -k by rsbx at 89.08.10.16.22.01.
  1178. X * 
  1179. X * Revision 4.3  89/05/01  15:13:02  narten
  1180. X@
  1181. SHAR_EOF
  1182. echo "extracting rcs/rcs.rcsfiles/rcslex.c,v"
  1183. sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcslex.c,v
  1184. Xhead     4.6;
  1185. Xbranch   4.6.2;
  1186. Xaccess   ;
  1187. Xsymbols  amiga_rcs:4.6.2 cbmvax_source:4.6.1 uunet_june89_dist:4.6;
  1188. Xlocks    ; strict;
  1189. Xcomment  @ * @;
  1190. X
  1191. X
  1192. X4.6
  1193. Xdate     89.05.01.15.13.07;  author narten;  state Exp;
  1194. Xbranches 4.6.1.1 4.6.2.1;
  1195. Xnext     ;
  1196. X
  1197. X4.6.1.1
  1198. Xdate     89.08.11.01.42.52;  author rsbx;  state Exp;
  1199. Xbranches ;
  1200. Xnext     ;
  1201. X
  1202. X4.6.2.1
  1203. Xdate     89.10.13.19.18.54;  author rsbx;  state Exp;
  1204. Xbranches ;
  1205. Xnext     4.6.2.2;
  1206. X
  1207. X4.6.2.2
  1208. Xdate     89.10.15.15.44.48;  author rsbx;  state Exp;
  1209. Xbranches ;
  1210. Xnext     ;
  1211. X
  1212. X
  1213. Xdesc
  1214. X@RCS file input.
  1215. X@
  1216. X
  1217. X
  1218. X
  1219. X4.6
  1220. Xlog
  1221. X@checked in with -k by rsbx at 89.08.10.16.22.16.
  1222. X@
  1223. Xtext
  1224. X@/*
  1225. X *                     RCS file input
  1226. X */
  1227. X#ifndef lint
  1228. Xstatic char rcsid[]= "$Id: rcslex.c,v 4.6 89/05/01 15:13:07 narten Exp $ Purdue CS";
  1229. X#endif
  1230. X/*********************************************************************************
  1231. X *                     Lexical Analysis.
  1232. X *                     Character mapping table,
  1233. X *                     hashtable, Lexinit, nextlex, getlex, getkey,
  1234. X *                     getid, getnum, readstring, printstring, savestring,
  1235. X *                     checkid, serror, fatserror, error, faterror, warn, diagnose
  1236. X *                     fflsbuf, puts, fprintf
  1237. X *                     Testprogram: define LEXDB
  1238. X *********************************************************************************
  1239. X */
  1240. X
  1241. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  1242. X * All rights reserved.
  1243. X *
  1244. X * Redistribution and use in source and binary forms are permitted
  1245. X * provided that the above copyright notice and this paragraph are
  1246. X * duplicated in all such forms and that any documentation,
  1247. X * advertising materials, and other materials related to such
  1248. X * distribution and use acknowledge that the software was developed
  1249. X * by Walter Tichy.
  1250. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  1251. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  1252. X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  1253. X *
  1254. X * Report all problems and direct all questions to:
  1255. X *   rcs-bugs@@cs.purdue.edu
  1256. X * 
  1257. X
  1258. X
  1259. X
  1260. X
  1261. X
  1262. X
  1263. X
  1264. X*/
  1265. X
  1266. X
  1267. X
  1268. X/* $Log:    rcslex.c,v $
  1269. X * Revision 4.6  89/05/01  15:13:07  narten
  1270. X * changed copyright header to reflect current distribution rules
  1271. X * 
  1272. X * Revision 4.5  88/11/08  12:00:54  narten
  1273. X * changes from  eggert@@sm.unisys.com (Paul Eggert)
  1274. X * 
  1275. X * Revision 4.5  88/08/28  15:01:12  eggert
  1276. X * Don't loop when writing error messages to a full filesystem.
  1277. X * Flush stderr/stdout when mixing output.
  1278. X * Yield exit status compatible with diff(1).
  1279. X * Shrink stdio code size; allow cc -R; remove lint.
  1280. X * 
  1281. X * Revision 4.4  87/12/18  11:44:47  narten
  1282. X * fixed to use "varargs" in "fprintf"; this is required if it is to
  1283. X * work on a SPARC machine such as a Sun-4
  1284. X * 
  1285. X * Revision 4.3  87/10/18  10:37:18  narten
  1286. X * Updating version numbers. Changes relative to 1.1 actually relative
  1287. X * to version 4.1
  1288. X * 
  1289. X * Revision 1.3  87/09/24  14:00:17  narten
  1290. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  1291. X * warnings)
  1292. X * 
  1293. X * Revision 1.2  87/03/27  14:22:33  jenkins
  1294. X * Port to suns
  1295. X * 
  1296. X * Revision 1.1  84/01/23  14:50:33  kcs
  1297. X * Initial revision
  1298. X * 
  1299. X * Revision 4.1  83/03/25  18:12:51  wft
  1300. X * Only changed $Header to $Id.
  1301. X * 
  1302. X * Revision 3.3  82/12/10  16:22:37  wft
  1303. X * Improved error messages, changed exit status on error to 1.
  1304. X *
  1305. X * Revision 3.2  82/11/28  21:27:10  wft
  1306. X * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
  1307. X * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
  1308. X * properly in case there is an IO-error (e.g., file system full).
  1309. X *
  1310. X * Revision 3.1  82/10/11  19:43:56  wft
  1311. X * removed unused label out:;
  1312. X * made sure all calls to getc() return into an integer, not a char.
  1313. X */
  1314. X
  1315. X
  1316. X/*
  1317. X#define LEXDB
  1318. X/* version LEXDB is for testing the lexical analyzer. The testprogram
  1319. X * reads a stream of lexemes, enters the revision numbers into the
  1320. X * hashtable, and prints the recognized tokens. Keywords are recognized
  1321. X * as identifiers.
  1322. X */
  1323. X
  1324. X
  1325. X
  1326. X#include "rcsbase.h"
  1327. X#include <varargs.h>
  1328. X
  1329. X
  1330. X
  1331. X/* character mapping table */
  1332. Xenum tokens map[] = {
  1333. X        EOFILE,         /* this will end up at ctab[-1] */
  1334. X        UNKN,   INSERT, UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
  1335. X        UNKN,   SPACE,  NEWLN,  UNKN,   SPACE,  UNKN,   UNKN,   UNKN,
  1336. X        UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
  1337. X        UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
  1338. X        SPACE,  EXCLA,  DQUOTE, HASH,   DOLLAR, PERCNT, AMPER,  SQUOTE,
  1339. X        LPARN,  RPARN,  TIMES,  PLUS,   COMMA,  MINUS,  PERIOD, DIVIDE,
  1340. X        DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,
  1341. X        DIGIT,  DIGIT,  COLON,  SEMI,   LESS,   EQUAL,  GREAT,  QUEST,
  1342. X        AT,     LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1343. X        LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1344. X        LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1345. X        LETTER, LETTER, LETTER, LBRACK, BACKSL, RBRACK, UPARR,  UNDER,
  1346. X        ACCENT, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1347. X        LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1348. X        LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
  1349. X        LETTER, LETTER, LETTER, LBRACE, BAR,    RBRACE, TILDE,  UNKN
  1350. X};
  1351. X
  1352. X
  1353. X
  1354. X
  1355. Xstruct hshentry * nexthsh;  /*pointer to next hashtable-entry, set by lookup*/
  1356. X
  1357. Xenum tokens     nexttok;    /*next token, set by nextlex                    */
  1358. X
  1359. Xint             hshenter;   /*if true, next suitable lexeme will be entered */
  1360. X                            /*into the symbol table. Handle with care.      */
  1361. Xint             nextc;      /*next input character, initialized by Lexinit  */
  1362. X
  1363. Xint             eof;        /*end-of-file indicator, set to >0 on end of file*/
  1364. Xint             line;       /*current line-number of input                  */
  1365. Xint             nerror;     /*counter for errors                            */
  1366. Xint             nwarn;      /*counter for warnings                          */
  1367. Xchar *          cmdid;      /*command identification for error messages     */
  1368. Xint             quietflag;  /*indicates quiet mode                          */
  1369. XFILE *          finptr;     /*input file descriptor                         */
  1370. X
  1371. XFILE *          frewrite;   /*file descriptor for echoing input             */
  1372. X
  1373. Xint             rewriteflag;/*indicates whether to echo to frewrite         */
  1374. X
  1375. Xchar            StringTab[strtsize]; /* string table and heap               */
  1376. X
  1377. Xchar *          NextString;         /*pointer to next identifier in StringTab*/
  1378. Xchar *          Topchar;            /*pointer to next free byte in StringTab*/
  1379. X                                    /*set by nextlex, lookup                */
  1380. Xstruct hshentry hshtab[hshsize];    /*hashtable                             */
  1381. X
  1382. X
  1383. X
  1384. X
  1385. X
  1386. Xlookup() {
  1387. X
  1388. X/* Function: Looks up the character string pointed to by NextString in the
  1389. X * hashtable. If the string is not present, a new entry for it is created.
  1390. X * If the string is present, TopChar is moved back to save the space for
  1391. X * the string, and NextString is set to point to the original string.
  1392. X * In any case, the address of the corresponding hashtable entry is placed
  1393. X * into nexthsh.
  1394. X * Algorithm: Quadratic hash, covering all entries.
  1395. X * Assumptions: NextString points at the first character of the string.
  1396. X * Topchar points at the first empty byte after the string.
  1397. X */
  1398. X
  1399. X        register int     ihash;      /* index into hashtable */
  1400. X        register char    * sp, * np;
  1401. X        int              c, delta, final, FirstScan; /*loop control*/
  1402. X
  1403. X        /* calculate hash code */
  1404. X        sp = NextString;
  1405. X        ihash = 0;
  1406. X        while (*sp) ihash += *sp++;
  1407. X
  1408. X        /* set up first search loop (c=0,step=1,until (hshsiz-1)/2 */
  1409. X        c=0;delta=1;final=(hshsize-1)/2;
  1410. X        FirstScan=true;   /*first loop */
  1411. X
  1412. X        for (;;) {
  1413. X                ihash = (ihash+c)%hshsize;   /*next index*/
  1414. X
  1415. X                if (hshtab[ihash].num == nil) {
  1416. X                        /*empty slot found*/
  1417. X                        hshtab[ihash].num = NextString;
  1418. X                        nexthsh= &hshtab[ihash];/*save hashtable address*/
  1419. X#                       ifdef LEXDB
  1420. X                        VOID printf("\nEntered: %s at %d ",nexthsh->num, ihash);
  1421. X#                       endif
  1422. X                        return;
  1423. X                }
  1424. X                /* compare strings */
  1425. X                sp=NextString;np=hshtab[ihash].num;
  1426. X                while (*sp == *np++) {
  1427. X                        if (*sp == 0) {
  1428. X                                /* match found */
  1429. X                                nexthsh= &hshtab[ihash];
  1430. X                                Topchar = NextString;
  1431. X                                NextString = nexthsh->num;
  1432. X                                return;
  1433. X                        } else sp++;
  1434. X                }
  1435. X
  1436. X                /* neither empty slot nor string found */
  1437. X                /* calculate next index and repeat */
  1438. X                if (c != final)
  1439. X                        c += delta;
  1440. X                else {
  1441. X                        if (FirstScan) {
  1442. X                                /*set up second sweep*/
  1443. X                                delta = -1; final = 1; FirstScan= false;
  1444. X                        } else {
  1445. X                                fatserror("Hashtable overflow");
  1446. X                        }
  1447. X                }
  1448. X        }
  1449. X};
  1450. X
  1451. X
  1452. X
  1453. X
  1454. X
  1455. X
  1456. XLexinit()
  1457. X/* Function: Initialization of lexical analyzer:
  1458. X * initializes the hastable,
  1459. X * initializes nextc, nexttok if finptr != NULL
  1460. X */
  1461. X{       register int            c;
  1462. X
  1463. X        for (c=hshsize-1; c>=0; c--) {
  1464. X                hshtab[c].num = nil;
  1465. X        }
  1466. X
  1467. X        hshenter=true; eof=0; line=1; nerror=0; nwarn=0;
  1468. X        NextString=nil; Topchar = &StringTab[0];
  1469. X        if (finptr) {
  1470. X                nextc = GETC(finptr,frewrite,rewriteflag); /*initial character*/
  1471. X                nextlex();            /*initial token*/
  1472. X        } else {
  1473. X                nextc = '\0';
  1474. X                nexttok=EOFILE;
  1475. X        }
  1476. X}
  1477. X
  1478. X
  1479. X
  1480. X
  1481. X
  1482. X
  1483. X
  1484. Xnextlex()
  1485. X
  1486. X/* Function: Reads the next token and sets nexttok to the next token code.
  1487. X * Only if the hshenter==true, a revision number is entered into the
  1488. X * hashtable and a pointer to it is placed into nexthsh.
  1489. X * This is useful for avoiding that dates are placed into the hashtable.
  1490. X * For ID's and NUM's, NextString is set to the character string in the
  1491. X * string table. Assumption: nextc contains the next character.
  1492. X */
  1493. X{       register c;
  1494. X    register FILE * fin, * frew;
  1495. X        register char * sp;
  1496. X        register enum tokens d;
  1497. X
  1498. X        if (eof) {
  1499. X                nexttok=EOFILE;
  1500. X                return;
  1501. X        }
  1502. X    fin=finptr; frew=frewrite;
  1503. Xloop:
  1504. X        switch(nexttok=ctab[nextc]) {
  1505. X
  1506. X        case UNKN:
  1507. X        case IDCHAR:
  1508. X        case PERIOD:
  1509. X                serror("unknown Character: %c",nextc);
  1510. X                nextc=GETC(fin,frew,rewriteflag);
  1511. X                goto loop;
  1512. X
  1513. X        case NEWLN:
  1514. X                line++;
  1515. X#               ifdef LEXDB
  1516. X                VOID putchar('\n');
  1517. X#               endif
  1518. X                /* Note: falls into next case */
  1519. X
  1520. X        case SPACE:
  1521. X                nextc=GETC(fin,frew,rewriteflag);
  1522. X                goto loop;
  1523. X
  1524. X        case EOFILE:
  1525. X                eof++;
  1526. X                nexttok=EOFILE;
  1527. X                return;
  1528. X
  1529. X        case DIGIT:
  1530. X                NextString = sp = Topchar;
  1531. X                *sp++ = nextc;
  1532. X                while ((d=ctab[c=GETC(fin,frew,rewriteflag)])==DIGIT ||
  1533. X                        d==PERIOD) {
  1534. X                        *sp++ = c;         /* 1.2. and 1.2 are different */
  1535. X                }
  1536. X                *sp++ = '\0';
  1537. X                if (sp >= StringTab+strtsize) {
  1538. X                        /*may have written outside stringtable already*/
  1539. X                        fatserror("Stringtable overflow");
  1540. X                }
  1541. X                Topchar = sp;
  1542. X                nextc = c;
  1543. X                if (hshenter == true)
  1544. X                        lookup();      /* lookup updates NextString, Topchar*/
  1545. X                nexttok = NUM;
  1546. X                return;
  1547. X
  1548. X
  1549. X        case LETTER:
  1550. X                NextString = sp = Topchar;
  1551. X                *sp++ = nextc;
  1552. X                while ((d=ctab[c=GETC(fin,frew,rewriteflag)])==LETTER ||
  1553. X                        d==DIGIT || d==IDCHAR) {
  1554. X                        *sp++ = c;
  1555. X                }
  1556. X                *sp++ = '\0';
  1557. X                if (sp >= StringTab+strtsize) {
  1558. X                        /*may have written outside stringtable already*/
  1559. X                        fatserror("Stringtable overflow");
  1560. X                }
  1561. X                Topchar = sp;
  1562. X                nextc = c;
  1563. X                nexttok = ID;  /* may be ID or keyword */
  1564. X                return;
  1565. X
  1566. X        case SBEGIN: /* long string */
  1567. X                nexttok = STRING;
  1568. X                /* note: only the initial SBEGIN has been read*/
  1569. X                /* read the string, and reset nextc afterwards*/
  1570. X                return;
  1571. X
  1572. X        default:
  1573. X                nextc=GETC(fin,frew,rewriteflag);
  1574. X                return;
  1575. X        }
  1576. X}
  1577. X
  1578. X
  1579. Xint getlex(token)
  1580. Xenum tokens token;
  1581. X/* Function: Checks if nexttok is the same as token. If so,
  1582. X * advances the input by calling nextlex and returns true.
  1583. X * otherwise returns false.
  1584. X * Doesn't work for strings and keywords; loses the character string for ids.
  1585. X */
  1586. X{
  1587. X        if (nexttok==token) {
  1588. X                nextlex();
  1589. X                return(true);
  1590. X        } else  return(false);
  1591. X}
  1592. X
  1593. Xint getkey (key)
  1594. Xchar * key;
  1595. X/* Function: If the current token is a keyword identical to key,
  1596. X * getkey advances the input by calling nextlex and returns true;
  1597. X * otherwise returns false.
  1598. X */
  1599. X{
  1600. X        register char *s1,*s2;
  1601. X
  1602. X        if (nexttok==ID) {
  1603. X                s1=key; s2=NextString;
  1604. X                while(*s1 == *s2++)
  1605. X                     if (*s1++ == '\0') {
  1606. X                         /* match found */
  1607. X                         Topchar = NextString; /*reset Topchar */
  1608. X                         nextlex();
  1609. X                         return(true);
  1610. X                     }
  1611. X        }
  1612. X        return(false);
  1613. X}
  1614. X
  1615. X
  1616. X
  1617. Xchar * getid()
  1618. X/* Function: Checks if nexttok is an identifier. If so,
  1619. X * advances the input by calling nextlex and returns a pointer
  1620. X * to the identifier; otherwise returns nil.
  1621. X * Treats keywords as identifiers.
  1622. X */
  1623. X{
  1624. X        register char * name;
  1625. X        if (nexttok==ID) {
  1626. X                name = NextString;
  1627. X                nextlex();
  1628. X                return name;
  1629. X        } else  return nil;
  1630. X}
  1631. X
  1632. X
  1633. Xstruct hshentry * getnum()
  1634. X/* Function: Checks if nexttok is a number. If so,
  1635. X * advances the input by calling nextlex and returns a pointer
  1636. X * to the hashtable entry. Otherwise returns nil.
  1637. X * Doesn't work if hshenter is false.
  1638. X */
  1639. X{
  1640. X        register struct hshentry * num;
  1641. X        if (nexttok==NUM) {
  1642. X                num=nexthsh;
  1643. X                nextlex();
  1644. X                return num;
  1645. X        } else  return nil;
  1646. X}
  1647. X
  1648. X
  1649. Xreadstring()
  1650. X/* skip over characters until terminating single SDELIM        */
  1651. X/* if rewriteflag==true, copy every character read to frewrite.*/
  1652. X/* Does not advance nextlex at the end.                        */
  1653. X{       register c;
  1654. X    register FILE * fin,  * frew;
  1655. X    fin=finptr; frew=frewrite;
  1656. X        if (rewriteflag) {
  1657. X                /* copy string verbatim to frewrite */
  1658. X                while ((c=getc(fin)) != EOF) {
  1659. X            VOID putc(c,frew);
  1660. X                        if (c==SDELIM) {
  1661. X                                if ((c=getc(fin)) == EOF || putc(c,frew) != SDELIM) {
  1662. X                                        /* end of string */
  1663. X                                        nextc=c;
  1664. X                                        return;
  1665. X                                }
  1666. X                        }
  1667. X                }
  1668. X        } else {
  1669. X                /* skip string */
  1670. X                while ((c=getc(fin)) != EOF) {
  1671. X                        if (c==SDELIM) {
  1672. X                                if ((c=getc(fin)) != SDELIM) {
  1673. X                                        /* end of string */
  1674. X                                        nextc=c;
  1675. X                                        return;
  1676. X                                }
  1677. X                        }
  1678. X                }
  1679. X        }
  1680. X        nextc = c;
  1681. X        error("Unterminated string");
  1682. X}
  1683. X
  1684. X
  1685. Xprintstring()
  1686. X/* Function: copy a string to stdout, until terminated with a single SDELIM.
  1687. X * Does not advance nextlex at the end.
  1688. X */
  1689. X{
  1690. X        register c;
  1691. X    register FILE * fin;
  1692. X    fin=finptr;
  1693. X    while ((c=getc(fin)) != EOF) {
  1694. X                if (c==SDELIM) {
  1695. X            if ((c=getc(fin)) != SDELIM) {
  1696. X                                /* end of string */
  1697. X                                nextc=c;
  1698. X                                return;
  1699. X                        }
  1700. X                }
  1701. X                VOID putchar(c);
  1702. X        }
  1703. X        nextc = c;
  1704. X        error("Unterminated string");
  1705. X}
  1706. X
  1707. X
  1708. X
  1709. Xsavestring(target,length)
  1710. Xchar * target; int length;
  1711. X/* copies a string terminated with SDELIM from file finptr to buffer target,
  1712. X * but not more than length bytes. If the string is longer than length,
  1713. X * the extra characters are skipped. The string may be empty, in which
  1714. X * case a '\0' is placed into target.
  1715. X * Double SDELIM is replaced with SDELIM.
  1716. X * If rewriteflag==true, the string is also copied unchanged to frewrite.
  1717. X * Returns the length of the saved string.
  1718. X * Does not advance nextlex at the end.
  1719. X */
  1720. X{
  1721. X        register c;
  1722. X    register FILE * fin, * frew;
  1723. X        register char * tp, * max;
  1724. X
  1725. X    fin=finptr; frew=frewrite;
  1726. X        tp=target; max= target+length; /*max is one too large*/
  1727. X        while ((c=GETC(fin,frew,rewriteflag))!=EOF) {
  1728. X        *tp++ =c;
  1729. X                if (c== SDELIM) {
  1730. X                        if ((c=GETC(fin,frew,rewriteflag))!=SDELIM) {
  1731. X                                /* end of string */
  1732. X                                *(tp-1)='\0';
  1733. X                                nextc=c;
  1734. X                                return;
  1735. X                        }
  1736. X                }
  1737. X                if (tp >= max) {
  1738. X                        /* overflow */
  1739. X                        error("string buffer overflow -- truncating string");
  1740. X                        target[length-1]='\0';
  1741. X                        /* skip rest of string */
  1742. X                        while ((c=GETC(fin,frew,rewriteflag))!=EOF) {
  1743. X                                if ((c==SDELIM) && ((c=GETC(fin,frew,rewriteflag))!=SDELIM)) {
  1744. X                                        /* end of string */
  1745. X                                        nextc=c;
  1746. X                                        return;
  1747. X                                }
  1748. X                        }
  1749. X                        nextc = c;
  1750. X                        error("Can't find %c to terminate string before end of file",SDELIM);
  1751. X                        return;
  1752. X                }
  1753. X        }
  1754. X        nextc = c;
  1755. X        error("Can't find %c to terminate string before end of file",SDELIM);
  1756. X}
  1757. X
  1758. X
  1759. Xchar  *checkid(id, delim)
  1760. Xchar    *id, delim;
  1761. X/*   Function:  check whether the string starting at id is an   */
  1762. X/*              identifier and return a pointer to the last char*/
  1763. X/*              of the identifer. White space, delim and '\0'   */
  1764. X/*              are legal delimeters. Aborts the program if not */
  1765. X/*              a legal identifier. Useful for checking commands*/
  1766. X{
  1767. X        register enum  tokens  d;
  1768. X        register char    *temp;
  1769. X        register char    c,tc;
  1770. X
  1771. X        temp = id;
  1772. X        if ( ctab[*id] == LETTER ) {
  1773. X            while( (d=ctab[c=(*++id)]) == LETTER || d==DIGIT || d==IDCHAR) ;
  1774. X            if ( c!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) {
  1775. X                /* append \0 to end of id before error message */
  1776. X                tc = c;
  1777. X                while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1778. X                *id = '\0';
  1779. X                faterror("Invalid character %c in identifier %s",tc,temp);
  1780. X                return nil ;
  1781. X            } else
  1782. X                return id;
  1783. X        } else {
  1784. X            /* append \0 to end of id before error message */
  1785. X            while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
  1786. X            *id = '\0';
  1787. X            faterror("Identifier %s does not start with letter",temp);
  1788. X            return nil;
  1789. X        }
  1790. X}
  1791. X
  1792. Xwriteerror()
  1793. X{
  1794. X    static looping;
  1795. X    if (looping)
  1796. X        exit(2);
  1797. X    looping = 1;
  1798. X    faterror("write error");
  1799. X}
  1800. X
  1801. Xnlflush(iop)
  1802. Xregister FILE * iop;
  1803. X{
  1804. X    if (putc('\n',iop)==EOF || fflush(iop)==EOF)
  1805. X                writeerror();
  1806. X}
  1807. X
  1808. X
  1809. X/*VARARGS1*/
  1810. Xserror(e,e1,e2,e3,e4,e5)
  1811. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1812. X/* non-fatal syntax error */
  1813. X{       nerror++;
  1814. X        VOID fprintf(stderr,"%s error, line %d: ", cmdid, line);
  1815. X        VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1816. X        nlflush(stderr);
  1817. X}
  1818. X
  1819. X/*VARARGS1*/
  1820. Xerror(e,e1,e2,e3,e4,e5)
  1821. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1822. X/* non-fatal error */
  1823. X{       nerror++;
  1824. X        VOID fprintf(stderr,"%s error: ",cmdid);
  1825. X        VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1826. X        nlflush(stderr);
  1827. X}
  1828. X
  1829. X/*VARARGS1*/
  1830. Xfatserror(e,e1,e2,e3,e4,e5)
  1831. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1832. X/* fatal syntax error */
  1833. X{       nerror++;
  1834. X        VOID fprintf(stderr,"%s error, line %d: ", cmdid,line);
  1835. X        VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1836. X        VOID fprintf(stderr,"\n%s aborted\n",cmdid);
  1837. X        VOID cleanup();
  1838. X        exit(2);
  1839. X}
  1840. X
  1841. X/*VARARGS1*/
  1842. Xfaterror(e,e1,e2,e3,e4,e5)
  1843. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1844. X/* fatal error, terminates program after cleanup */
  1845. X{       nerror++;
  1846. X        VOID fprintf(stderr,"%s error: ",cmdid);
  1847. X        VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1848. X        VOID fprintf(stderr,"\n%s aborted\n",cmdid);
  1849. X        VOID cleanup();
  1850. X        exit(2);
  1851. X}
  1852. X
  1853. X/*VARARGS1*/
  1854. Xwarn(e,e1,e2,e3,e4,e5)
  1855. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1856. X/* prints a warning message */
  1857. X{       nwarn++;
  1858. X        VOID fprintf(stderr,"%s warning: ",cmdid);
  1859. X        VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1860. X        nlflush(stderr);
  1861. X}
  1862. X
  1863. X
  1864. X/*VARARGS1*/
  1865. Xdiagnose(e,e1,e2,e3,e4,e5)
  1866. Xchar * e, * e1, * e2, * e3, * e4, * e5;
  1867. X/* prints a diagnostic message */
  1868. X{
  1869. X        if (!quietflag) {
  1870. X                VOID fprintf(stderr,e, e1, e2, e3, e4, e5);
  1871. X                nlflush(stderr);
  1872. X        }
  1873. X}
  1874. X
  1875. X
  1876. X
  1877. Xfflsbuf(c, iop)
  1878. Xunsigned c; register FILE * iop;
  1879. X/* Function: Flush iop.
  1880. X * Same routine as _flsbuf in stdio, but aborts program on error.
  1881. X */
  1882. X{       register result;
  1883. X        if ((result=_flsbuf(c,iop))==EOF)
  1884. X                writeerror();
  1885. X        return result;
  1886. X}
  1887. X
  1888. X
  1889. Xfputs(s, iop)
  1890. Xregister char *s;
  1891. Xregister FILE *iop;
  1892. X/* Function: Put string s on file iop, abort on error.
  1893. X * Same as puts in stdio, but with different putc macro.
  1894. X */
  1895. X{
  1896. X    register r;
  1897. X    register c;
  1898. X
  1899. X    while (c = *s++)
  1900. X        r = putc(c, iop);
  1901. X    return(r);
  1902. X}
  1903. X
  1904. X
  1905. X
  1906. Xfprintf(iop, fmt, va_alist)
  1907. XFILE *iop;
  1908. Xchar *fmt;
  1909. Xva_dcl
  1910. X/* Function: formatted output. Same as fprintf in stdio,
  1911. X * but aborts program on error
  1912. X */
  1913. X{
  1914. X    register int value;
  1915. X    va_list ap;
  1916. X
  1917. X    va_start(ap);
  1918. X#ifdef VFPRINTF
  1919. X    VOID vfprintf(iop, fmt, ap);
  1920. X#else
  1921. X    _doprnt(fmt, ap, iop);
  1922. X#endif
  1923. X        if (ferror(iop)) {
  1924. X        writeerror();
  1925. X                value = EOF;
  1926. X        } else value = 0;
  1927. X    va_end(ap);
  1928. X    return value;
  1929. X}
  1930. X
  1931. X
  1932. X
  1933. X#ifdef LEXDB
  1934. X/* test program reading a stream of lexems and printing the tokens.
  1935. X */
  1936. X
  1937. X
  1938. X
  1939. Xmain(argc,argv)
  1940. Xint argc; char * argv[];
  1941. X{
  1942. X        cmdid="lextest";
  1943. X        if (argc<2) {
  1944. X                VOID fputs("No input file\n",stderr);
  1945. X                exit(1);
  1946. X        }
  1947. X        if ((finptr=fopen(argv[1], "r")) == NULL) {
  1948. X                faterror("Can't open input file %s\n",argv[1]);
  1949. X        }
  1950. X        Lexinit();
  1951. X        rewriteflag=false;
  1952. X        while (nexttok != EOFILE) {
  1953. X        switch (nexttok) {
  1954. X
  1955. X        case ID:
  1956. X                VOID printf("ID: %s",NextString);
  1957. X                break;
  1958. X
  1959. X        case NUM:
  1960. X                if (hshenter==true)
  1961. X                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
  1962. X                else
  1963. X                   VOID printf("NUM, unentered: %s",NextString);
  1964. X                hshenter = !hshenter; /*alternate between dates and numbers*/
  1965. X                break;
  1966. X
  1967. X        case COLON:
  1968. X                VOID printf("COLON"); break;
  1969. X
  1970. X        case SEMI:
  1971. X                VOID printf("SEMI"); break;
  1972. X
  1973. X        case STRING:
  1974. X                readstring();
  1975. X                VOID printf("STRING"); break;
  1976. X
  1977. X        case UNKN:
  1978. X                VOID printf("UNKN"); break;
  1979. X
  1980. X        default:
  1981. X                VOID printf("DEFAULT"); break;
  1982. X        }
  1983. X        VOID printf(" | ");
  1984. X        nextlex();
  1985. X        }
  1986. X        VOID printf("\nEnd of lexical analyzer test\n");
  1987. X}
  1988. X
  1989. Xcleanup()
  1990. X/* dummy */
  1991. X{}
  1992. X
  1993. X
  1994. X#endif
  1995. X@
  1996. X
  1997. X
  1998. X4.6.2.1
  1999. Xlog
  2000. X@Start of Amiga RCS port branch.
  2001. X@
  2002. Xtext
  2003. X@d5 1
  2004. Xa5 5
  2005. X<<<<<<< rcslex.c
  2006. Xstatic char rcsid[]= "$Id: rcslex.c,v 4.6.1.1 89/08/11 01:42:52 rsbx Exp Locker: rsbx $ Purdue CS";
  2007. X=======
  2008. Xstatic char rcsid[]= "$Id: rcslex.c,v 1.2 89/09/17 13:36:11 rick Exp $ Purdue CS";
  2009. X>>>>>>> 1.2
  2010. Xa45 11
  2011. X<<<<<<< rcslex.c
  2012. X * Revision 4.6.1.1  89/08/11  01:42:52  rsbx
  2013. X * Start of cbmvax RCS source branch.
  2014. X=======
  2015. X * Revision 1.2  89/09/17  13:36:11  rick
  2016. X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
  2017. X * All changes done with conditional compile (#ifdef AMIGA).  This version
  2018. X * compiles correctly with Lattice C version 5.02 or later.
  2019. X>>>>>>> 1.2
  2020. X * 
  2021. X<<<<<<< rcslex.c
  2022. Xa46 3
  2023. X * checked in with -k by rsbx at 89.08.10.16.22.16.
  2024. X * 
  2025. X * Revision 4.6  89/05/01  15:13:07  narten
  2026. Xa57 5
  2027. X=======
  2028. X * Revision 1.2  88/09/03  15:11:38  rick
  2029. X * Port to AmigaDos.  All done with conditional compiles
  2030. X * 
  2031. X>>>>>>> 1.2
  2032. Xa103 1
  2033. X#ifndef AMIGA
  2034. Xa104 1
  2035. X#endif
  2036. Xd226 1
  2037. Xa226 1
  2038. X}
  2039. Xa647 1
  2040. X<<<<<<< rcslex.c
  2041. Xa648 4
  2042. X=======
  2043. X                VOID putc('\n',stderr);
  2044. X                fflush(stderr);
  2045. X>>>>>>> 1.2
  2046. Xd653 1
  2047. Xa653 1
  2048. X#ifndef AMIGA
  2049. Xd682 1
  2050. Xd707 1
  2051. Xa707 1
  2052. X#endif
  2053. X@
  2054. X
  2055. X
  2056. X4.6.2.2
  2057. Xlog
  2058. X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  2059. Xsources I have here (and are later than the ones Rick used).
  2060. X@
  2061. Xtext
  2062. X@d5 5
  2063. Xa9 1
  2064. Xstatic char rcsid[]= "$Id: rcslex.c,v 4.6.2.1 89/10/13 19:18:54 rsbx Exp Locker: rsbx $ Purdue CS";
  2065. Xd50 1
  2066. Xa50 3
  2067. X * Revision 4.6.2.1  89/10/13  19:18:54  rsbx
  2068. X * Start of Amiga RCS port branch.
  2069. X * 
  2070. Xd53 6
  2071. Xd60 1
  2072. Xd76 5
  2073. Xd673 1
  2074. Xd675 4
  2075. X@
  2076. X
  2077. X
  2078. X4.6.1.1
  2079. Xlog
  2080. X@Start of cbmvax RCS source branch.
  2081. X@
  2082. Xtext
  2083. X@a46 3
  2084. X * checked in with -k by rsbx at 89.08.10.16.22.16.
  2085. X * 
  2086. X * Revision 4.6  89/05/01  15:13:07  narten
  2087. X@
  2088. SHAR_EOF
  2089. echo "extracting rcs/rcs.rcsfiles/rcsrev.c,v"
  2090. sed 's/^X//' << \SHAR_EOF > rcs/rcs.rcsfiles/rcsrev.c,v
  2091. Xhead     4.5;
  2092. Xbranch   4.5.2;
  2093. Xaccess   ;
  2094. Xsymbols  amiga_rcs:4.5.2 cbmvax_source:4.5.1 uunet_june89_dist:4.5;
  2095. Xlocks    ; strict;
  2096. Xcomment  @ * @;
  2097. X
  2098. X
  2099. X4.5
  2100. Xdate     89.05.01.15.13.22;  author narten;  state Exp;
  2101. Xbranches 4.5.1.1 4.5.2.1;
  2102. Xnext     ;
  2103. X
  2104. X4.5.1.1
  2105. Xdate     89.08.11.01.42.59;  author rsbx;  state Exp;
  2106. Xbranches ;
  2107. Xnext     4.5.1.2;
  2108. X
  2109. X4.5.1.2
  2110. Xdate     89.08.11.03.07.56;  author rsbx;  state Exp;
  2111. Xbranches ;
  2112. Xnext     ;
  2113. X
  2114. X4.5.2.1
  2115. Xdate     89.10.13.19.19.08;  author rsbx;  state Exp;
  2116. Xbranches ;
  2117. Xnext     4.5.2.2;
  2118. X
  2119. X4.5.2.2
  2120. Xdate     89.10.15.15.44.58;  author rsbx;  state Exp;
  2121. Xbranches ;
  2122. Xnext     ;
  2123. X
  2124. X
  2125. Xdesc
  2126. X@RCS revision number handling.
  2127. X@
  2128. X
  2129. X
  2130. X
  2131. X4.5
  2132. Xlog
  2133. X@checked in with -k by rsbx at 89.08.10.16.22.50.
  2134. X@
  2135. Xtext
  2136. X@/*
  2137. X *                     RCS revision number handling
  2138. X */
  2139. X#ifndef lint
  2140. Xstatic char rcsid[]= "$Id: rcsrev.c,v 4.5 89/05/01 15:13:22 narten Exp $ Purdue CS";
  2141. X#endif
  2142. X
  2143. X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2144. X * All rights reserved.
  2145. X *
  2146. X * Redistribution and use in source and binary forms are permitted
  2147. X * provided that the above copyright notice and this paragraph are
  2148. X * duplicated in all such forms and that any documentation,
  2149. X * advertising materials, and other materials related to such
  2150. X * distribution and use acknowledge that the software was developed
  2151. X * by Walter Tichy.
  2152. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  2153. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  2154. X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  2155. X *
  2156. X * Report all problems and direct all questions to:
  2157. X *   rcs-bugs@@cs.purdue.edu
  2158. X * 
  2159. X
  2160. X
  2161. X
  2162. X
  2163. X
  2164. X
  2165. X
  2166. X*/
  2167. X
  2168. X
  2169. X
  2170. X
  2171. X/* $Log:    rcsrev.c,v $
  2172. X * Revision 4.5  89/05/01  15:13:22  narten
  2173. X * changed copyright header to reflect current distribution rules
  2174. X * 
  2175. X * Revision 4.4  87/12/18  11:45:22  narten
  2176. X * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, 
  2177. X * since there's now a return value there with a value. (Guy Harris)
  2178. X * 
  2179. X * Revision 4.3  87/10/18  10:38:42  narten
  2180. X * Updating version numbers. Changes relative to version 1.1 actually 
  2181. X * relative to 4.1
  2182. X * 
  2183. X * Revision 1.3  87/09/24  14:00:37  narten
  2184. X * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  2185. X * warnings)
  2186. X * 
  2187. X * Revision 1.2  87/03/27  14:22:37  jenkins
  2188. X * Port to suns
  2189. X * 
  2190. X * Revision 1.1  84/01/23  14:50:37  kcs
  2191. X * Initial revision
  2192. X * 
  2193. X * Revision 4.1  83/03/25  21:10:45  wft
  2194. X * Only changed $Header to $Id.
  2195. X * 
  2196. X * Revision 3.4  82/12/04  13:24:08  wft
  2197. X * Replaced getdelta() with gettree().
  2198. X *
  2199. X * Revision 3.3  82/11/28  21:33:15  wft
  2200. X * fixed compartial() and compnum() for nil-parameters; fixed nils
  2201. X * in error messages. Testprogram output shortenend.
  2202. X *
  2203. X * Revision 3.2  82/10/18  21:19:47  wft
  2204. X * renamed compnum->cmpnum, compnumfld->cmpnumfld,
  2205. X * numericrevno->numricrevno.
  2206. X *
  2207. X * Revision 3.1  82/10/11  19:46:09  wft
  2208. X * changed expandsym() to check for source==nil; returns zero length string
  2209. X * in that case.
  2210. X */
  2211. X
  2212. X
  2213. X
  2214. X/*
  2215. X#define REVTEST
  2216. X/* version REVTEST is for testing the routines that generate a sequence
  2217. X * of delta numbers needed to regenerate a given delta.
  2218. X */
  2219. X
  2220. X#include "rcsbase.h"
  2221. X
  2222. Xextern FILE * finptr;   /* RCS input file */
  2223. Xextern char * getid();
  2224. Xextern struct hshentry * getnum();
  2225. Xextern int    getkey();
  2226. Xextern int    getlex();
  2227. X
  2228. Xextern char * getkeyval();
  2229. Xstruct hshentry * genbranch(); /* forward */
  2230. X
  2231. X
  2232. X
  2233. Xint countnumflds(s)
  2234. Xchar * s;
  2235. X/* Given a pointer s to a dotted number (date or revision number),
  2236. X * countnumflds returns the number of digitfields in s.
  2237. X */
  2238. X{       register char * sp;
  2239. X        register int    count;
  2240. X        if ((sp=s)==nil) return(0);
  2241. X        if (*sp == '\0') return(0);
  2242. X        count = 1;
  2243. X        while (*sp) {
  2244. X                if (*sp++ == '.') count++;
  2245. X        }
  2246. X        if (*(--sp) == '.') count--; /*trailing periods don't count*/
  2247. X        return(count);
  2248. X}
  2249. X
  2250. Xgetbranchno(revno,branchno)
  2251. Xchar * revno, * branchno;
  2252. X/* Given a non-nil revision number revno, getbranchno copies the number of the branch
  2253. X * on which revno is into branchnumber. If revno itself is a branch number,
  2254. X * it is copied unchanged.
  2255. X */
  2256. X{
  2257. X        register int i, numflds;
  2258. X        register char * tp, * sp;
  2259. X
  2260. X        numflds=countnumflds(revno);
  2261. X        if (numflds%2 == 1)
  2262. X                VOID strcpy(branchno,revno);
  2263. X        else {
  2264. X                sp=revno; tp=branchno;
  2265. X                for (i=1;i<numflds;i++) {
  2266. X                        while(*sp!='.') *tp++ = *sp++;
  2267. X                        *tp++ = *sp++;
  2268. X                }
  2269. X                *(tp-1)='\0';
  2270. X        }
  2271. X}
  2272. X
  2273. X
  2274. X
  2275. Xint cmpnum(num1, num2)
  2276. Xchar * num1, * num2;
  2277. X/* compares the two dotted numbers num1 and num2 lexicographically
  2278. X * by field. Individual fields are compared numerically.
  2279. X * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
  2280. X * omitted fields are assumed to be higher than the existing ones.
  2281. X*/
  2282. X{
  2283. X        register char * s1, *s2;
  2284. X        register int n1, n2;
  2285. X
  2286. X        s1=num1==nil?"":num1;
  2287. X        s2=num2==nil?"":num2;
  2288. X
  2289. X        do {
  2290. X                n1 = 0;
  2291. X                while (('0' <= *s1) && (*s1 <= '9')) {
  2292. X                        n1 = n1*10 + (*s1 - '0');
  2293. X                        s1++;
  2294. X                }
  2295. X                /* skip '.' */
  2296. X                if (*s1=='.') s1++;
  2297. X
  2298. X                n2 = 0;
  2299. X                while (('0' <= *s2) && (*s2 <= '9')) {
  2300. X                        n2 = n2*10 + (*s2 - '0');
  2301. X                        s2++;
  2302. X                }
  2303. X                /* skip '.' */
  2304. X                if (*s2=='.') s2++;
  2305. X
  2306. X        } while ((n1==n2) && (*s1!='\0') && (*s2!='\0'));
  2307. X
  2308. X        if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2))
  2309. X                return (n1 - n2);
  2310. X        /*now n1==n2 and one of s1 or s2 is shorter*/
  2311. X        /*give precedence to shorter one*/
  2312. X        if (*s1=='\0') return 1;
  2313. X        else           return -1;
  2314. X
  2315. X}
  2316. X
  2317. X
  2318. X
  2319. Xint cmpnumfld(num1, num2, fld)
  2320. Xchar * num1, * num2; int fld;
  2321. X/* compares the two dotted numbers at field fld and returns
  2322. X * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields.
  2323. X*/
  2324. X{
  2325. X        register char * s1, *s2;
  2326. X        register int n1, n2;
  2327. X
  2328. X        s1=num1; n1=fld-1;
  2329. X        /* skip fld-1 fields */
  2330. X        while (n1) {
  2331. X                while(*s1 != '.') s1++;
  2332. X                n1--; s1++;
  2333. X        }
  2334. X        s2 = num2; n2=fld-1;
  2335. X        while (n2) {
  2336. X                while(*s2 != '.') s2++;
  2337. X                n2--; s2++;
  2338. X        }
  2339. X        /* Don't put the above into a single loop! */
  2340. X        /* Now s1 and s2 point to the beginning of the respective fields */
  2341. X        /* compute numerical value and compare */
  2342. X        n1 = 0;
  2343. X        while (('0' <= *s1) && (*s1 <= '9')) {
  2344. X                n1 = n1*10 + (*s1 - '0');
  2345. X                s1++;
  2346. X        }
  2347. X        n2 = 0;
  2348. X        while (('0' <= *s2) && (*s2 <= '9')) {
  2349. X                n2 = n2*10 + (*s2 - '0');
  2350. X                s2++;
  2351. X        }
  2352. X        return (n1 - n2);
  2353. X}
  2354. X
  2355. X
  2356. Xint compartial(num1, num2, length)
  2357. Xchar    * num1;
  2358. Xchar    * num2;
  2359. Xint     length;
  2360. X
  2361. X/*   compare the first "length" fields of two dot numbers;
  2362. X     the omitted field is considered to be larger than any number  */
  2363. X/*   restriction:  at least one number has length or more fields   */
  2364. X
  2365. X{
  2366. X        register        char    *s1, *s2;
  2367. X        register        int     n1, n2;
  2368. X
  2369. X
  2370. X        s1 = num1;      s2 = num2;
  2371. X        if ( s1==nil || *s1 == '\0' ) return 1;
  2372. X        if ( s2==nil || *s2 == '\0' ) return -1;
  2373. X
  2374. X        do {
  2375. X            n1 = 0;
  2376. X            while( ('0' <= *s1) && (*s1 <= '9') ) {
  2377. X                n1 = n1 * 10 + (*s1 - '0') ;
  2378. X                s1++;
  2379. X            }
  2380. X            if ( *s1 == '.' ) s1++;    /*  skip .   */
  2381. X
  2382. X            n2 = 0;
  2383. X            while( ( '0' <= *s2) && ( *s2 <= '9' ) ) {
  2384. X                   n2 = n2 * 10 + ( *s2 - '0' ) ;
  2385. X                s2++;
  2386. X            }
  2387. X            if (*s2 == '.') s2++;
  2388. X        }   while(  ( n1 == n2) && ((--length) != 0) &&
  2389. X                    ( *s1 != '\0') && (*s2 != '\0')  );
  2390. X
  2391. X        if ( (n1 != n2) || (length == 0) ){
  2392. X                return(n1-n2);   }
  2393. X
  2394. X        if ( *s1 == '\0' ) return 1;
  2395. X        if ( *s2 == '\0' ) return -1;
  2396. X    VOID fprintf(stderr, "RCS Internal error, routine: compartial\n");
  2397. X    return(0);
  2398. X}
  2399. X
  2400. X
  2401. X
  2402. Xincnum(onum,nnum)
  2403. Xchar * onum, *nnum;
  2404. X/* increments the last field of revision number onum by one and
  2405. X * places the result into nnum
  2406. X */
  2407. X{
  2408. X        register char * sp, *tp;
  2409. X        register int i;
  2410. X
  2411. X        sp = onum; tp = nnum;
  2412. X        for (i=countnumflds(onum)-1; i>0; i--) {
  2413. X                while (*sp != '.') *tp++ = *sp++;
  2414. X                *tp++ = *sp++;  /* copy dot also */
  2415. X        }
  2416. X        VOID sprintf(tp,"%d",atoi(sp)+1);
  2417. X}
  2418. X
  2419. X
  2420. Xchar * partialno(rev1,rev2,length)
  2421. Xchar * rev1, * rev2; register int length;
  2422. X/* Function: Copies length fields of revision number rev2 into rev1.
  2423. X * returns rev1.
  2424. X */
  2425. X{       register char * r1,* r2;
  2426. X
  2427. X        r1=rev1; r2=rev2;
  2428. X        while (length) {
  2429. X                while(*r2 != '.' && *r2!='\0') *r1++ = *r2++;
  2430. X                *r1++ = *r2++;
  2431. X                length--;
  2432. X        }
  2433. X        /* eliminate last '.'*/
  2434. X        *(r1-1)='\0';
  2435. X        return rev1;
  2436. X}
  2437. X
  2438. X
  2439. X
  2440. Xchar * getancestor(r1, r2, r3)
  2441. Xchar * r1, *r2, *r3;
  2442. X/* function: finds the common ancestor of r1 and r2 and
  2443. X * places it into r3.
  2444. X * returns r3 if successful, false otherwise.
  2445. X * works reliably only if r1 and r2 are not branch numbers.
  2446. X */
  2447. X{       int l1, l2, l3;
  2448. X        char t1[revlength], t2[revlength];
  2449. X
  2450. X        l1=countnumflds(r1); l2=countnumflds(r2);
  2451. X        if ((l1<=2 && l2<=2)||(cmpnum(r1,r2)==0)) {
  2452. X                /* on main trunk or identical */
  2453. X                error("Common ancestor of %s and %s undefined.", r1, r2);
  2454. X                return false;
  2455. X        }
  2456. X
  2457. X        l3=0;
  2458. X        while ((cmpnumfld(r1, r2, l3+1)==0) && (cmpnumfld(r1, r2, l3+2)==0)){
  2459. X                l3=l3+2;
  2460. X        }
  2461. X        /* This will terminate since r1 and r2 are not the same; see above*/
  2462. X        if (l3==0) {
  2463. X                /* no common prefix. Common ancestor on main trunk. */
  2464. X                VOID partialno(t1,r1,l1>2?2:l1); VOID partialno(t2,r2,l2>2?2:l2);
  2465. X                if (cmpnum(t1,t2)<0)
  2466. X                        VOID strcpy(r3,t1);
  2467. X                else    VOID strcpy(r3,t2);
  2468. X                if ((cmpnum(r3,r1)==0)||(cmpnum(r3,r2)==0)) {
  2469. X                        error("Ancestor for %s and %s undefined.",r1,r2);
  2470. X                        return false;
  2471. X                }
  2472. X                return r3;
  2473. X        } else {
  2474. X               if (cmpnumfld(r1,r2,l3+1)==0) {
  2475. X                        error("Ancestor for %s and %s undefined.",r1,r2);
  2476. X                        return false;
  2477. X                }
  2478. X                return(partialno(r3,r1,l3));
  2479. X        }
  2480. X}
  2481. X
  2482. X
  2483. X
  2484. X
  2485. Xstruct hshentry * genrevs(revno,date,author,state,store)
  2486. Xchar * revno, * date, * author, * state;
  2487. Xstruct hshentry * * store;
  2488. X/* Function: finds the deltas needed for reconstructing the
  2489. X * revision given by revno, date, author, and state, and stores pointers
  2490. X * to these deltas into an array whose starting address is given by store.
  2491. X * The last pointer stored is nil. The last delta (target delta) is returned.
  2492. X * If the proper delta could not be found, nil is returned.
  2493. X */
  2494. X{
  2495. X        int length;
  2496. X        register struct hshentry * next;
  2497. X        int result;
  2498. X        char * branchnum;
  2499. X        char t[revlength];
  2500. X
  2501. X        if (Head == nil) {
  2502. X                error("RCSfile empty.");
  2503. X                return nil;
  2504. X        }
  2505. X
  2506. X        length = countnumflds(revno);
  2507. X        next=Head;
  2508. X
  2509. X        if (length >= 1) {
  2510. X                /* at least one field; find branch exactly */
  2511. X                while ((next!=nil) &&
  2512. X                       ((result=cmpnumfld(revno,next->num,1))<0)) {
  2513. X                        /*puts(next->num);*/
  2514. X                        *store++ = next;
  2515. X                        next = next->next;
  2516. X                }
  2517. X
  2518. X                if (next==nil) {error("Branch number %s too low.",partialno(t,revno,1));return nil;}
  2519. X                if (result>0)  {error("Branch number %s not present.",partialno(t,revno,1));return nil;}
  2520. X        }
  2521. X        if (length<=1){
  2522. X                /* pick latest one on given branch */
  2523. X                branchnum = next->num; /* works even for empty revno*/
  2524. X                while ((next!=nil) &&
  2525. X                       (cmpnumfld(branchnum,next->num,1)==0) &&
  2526. X                       !(
  2527. X                        (date==nil?1:(cmpnum(date,next->date)>=0)) &&
  2528. X                        (author==nil?1:(strcmp(author,next->author)==0)) &&
  2529. X                        (state ==nil?1:(strcmp(state, next->state) ==0))
  2530. X                        )
  2531. X                       )
  2532. X                {       /*puts(next->num);*/
  2533. X                        *store ++ = next;
  2534. X                        next=next->next;
  2535. X                }
  2536. X                if ((next==nil) ||
  2537. X                    (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
  2538. X                        error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.",
  2539. X                                length==0?partialno(t,branchnum,1):revno,date==nil?"<now>":date,
  2540. X                                author==nil?"<any>":author, state==nil?"<any>":state);
  2541. X                        return nil;
  2542. X                } else {
  2543. X                        /*puts(next->num);*/
  2544. X                        *store++ = next;
  2545. X                }
  2546. X                *store = nil;
  2547. X                return next;
  2548. X        }
  2549. X
  2550. X        /* length >=2 */
  2551. X        /* find revision; may go low if length==2*/
  2552. X        while ((next!=nil) &&
  2553. X               ((result =cmpnumfld(revno,next->num,2)) <0) &&
  2554. X               (cmpnumfld(revno,next->num,1)==0) ) {
  2555. X                /*puts(next->num);*/
  2556. X                *store++ = next;
  2557. X                next = next->next;
  2558. X        }
  2559. X
  2560. X        if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
  2561. X                error("Revision number %s too low.",partialno(t,revno,2));
  2562. X                return nil;
  2563. X        }
  2564. X        if ((length>2) && (result!=0)) {
  2565. X                error("Revision %s not present.",partialno(t,revno,2));
  2566. X                return nil;
  2567. X        }
  2568. X
  2569. X        /* print last one */
  2570. X        /*puts(next->num);*/
  2571. X        *store++ = next;
  2572. X
  2573. X        if (length>2)
  2574. X                return genbranch(next,revno,length,date,author,state,store);
  2575. X        else { /* length == 2*/
  2576. X                if ((date!=nil) && (cmpnum(date,next->date)<0)){
  2577. X                        error("Revision %s has date %s.",next->num, next->date);
  2578. X                        return nil;
  2579. X                }
  2580. X                if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
  2581. X                        error("Revision %s has author %s.",next->num,next->author);
  2582. X                        return nil;
  2583. X                }
  2584. X                if ((state!=nil)&&(strcmp(state,next->state)!=0)) {
  2585. X                        error("Revision %s has state %s.",next->num,
  2586. X                               next->state==nil?"<empty>":next->state);
  2587. X                        return nil;
  2588. X                }
  2589. X                *store=nil;
  2590. X                return next;
  2591. X        }
  2592. X}
  2593. X
  2594. X
  2595. X
  2596. X
  2597. Xstruct hshentry * genbranch(bpoint, revno, length,date,author,state,store)
  2598. Xstruct hshentry * bpoint;
  2599. Xchar * revno; int length;
  2600. Xchar * date, * author, * state;
  2601. Xstruct hshentry ** store;
  2602. X/* Function: given a branchpoint, a revision number, date, author, and state,
  2603. X * genbranch finds the deltas necessary to reconstruct the given revision
  2604. X * from the branch point on.
  2605. X * Pointers to the found deltas are stored in an array beginning with store.
  2606. X * revno must be on a side branch.
  2607. X * return nil on error
  2608. X */
  2609. X{
  2610. X        int field;
  2611. X        register struct hshentry * next, * trail;
  2612. X        register struct branchhead * bhead;
  2613. X        int result;
  2614. X        char t[revlength];
  2615. X
  2616. X        bhead = bpoint->branches;
  2617. X
  2618. X        for (field=3; field<=length; field=field+2) {
  2619. X
  2620. X                if (bhead==nil) {error("No side branches present for %s.",partialno(t,revno,field-1));return nil;}
  2621. X
  2622. X                /*find branch head*/
  2623. X                /*branches are arranged in increasing order*/
  2624. X                while ((bhead!=nil) &&
  2625. X                       ((result=cmpnumfld(revno,bhead->hsh->num,field))>0)) {
  2626. X                        bhead = bhead->nextbranch;
  2627. X                }
  2628. X
  2629. X                if (bhead==nil) {error("Branch number %s too high.",partialno(t,revno,field));return nil;}
  2630. X                if (result<0)   {error("Branch number %s not present.",partialno(t,revno,field));return nil;}
  2631. X
  2632. X                next = bhead->hsh;
  2633. X                if (length==field) {
  2634. X                        /* pick latest one on that branch */
  2635. X                        trail=nil;
  2636. X                        do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) &&
  2637. X                                 (author==nil?1:(strcmp(author,next->author)==0)) &&
  2638. X                                 (state ==nil?1:(strcmp(state, next->state) ==0))
  2639. X                             ) trail = next;
  2640. X                             next=next->next;
  2641. X                        } while (next!=nil);
  2642. X
  2643. X                        if (trail==nil) {
  2644. X                             error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.",
  2645. X                                        revno, date==nil?"<now>":date,
  2646. X                                        author==nil?"<any>":author, state==nil?"<any>":state);
  2647. X                             return nil;
  2648. X                        } else { /* print up to last one suitable */
  2649. X                             next = bhead->hsh;
  2650. X                             while (next!=trail) {
  2651. X                                  /*puts(next->num);*/
  2652. X                                  *store++ = next;
  2653. X                                  next=next->next;
  2654. X                             }
  2655. X                             /*puts(next->num);*/
  2656. X                             *store++ = next;
  2657. X                        }
  2658. X                        *store = nil;
  2659. X                        return next;
  2660. X                }
  2661. X
  2662. X                /* length > field */
  2663. X                /* find revision */
  2664. X                /* check low */
  2665. X                if (cmpnumfld(revno,next->num,field+1)<0) {
  2666. X                        error("Revision number %s too low.",partialno(t,revno,field+1));
  2667. X                        return(nil);
  2668. X                }
  2669. X                do {    /*puts(next->num);*/
  2670. X                        *store++ = next;
  2671. X                        trail = next;
  2672. X                        next = next->next;
  2673. X                } while ((next!=nil) &&
  2674. X                       (cmpnumfld(revno,next->num,field+1) >=0));
  2675. X
  2676. X                if ((length>field+1) &&  /*need exact hit */
  2677. X                    (cmpnumfld(revno,trail->num,field+1) !=0)){
  2678. X                        error("Revision %s not present.",partialno(t,revno,field+1));
  2679. X                        return(nil);
  2680. X                }
  2681. X                if (length == field+1) {
  2682. X                        if ((date!=nil) && (cmpnum(date,trail->date)<0)){
  2683. X                                error("Revision %s has date %s.",trail->num, trail->date);
  2684. X                                return nil;
  2685. X                        }
  2686. X                        if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
  2687. X                                error("Revision %s has author %s.",trail->num,trail->author);
  2688. X                                return nil;
  2689. X                        }
  2690. X                        if ((state!=nil)&&(strcmp(state,trail->state)!=0)) {
  2691. X                                error("Revision %s has state %s.",trail->num,
  2692. X                                       trail->state==nil?"<empty>":trail->state);
  2693. X                                return nil;
  2694. X                        }
  2695. X                }
  2696. X                bhead = trail->branches;
  2697. X
  2698. X        }
  2699. X        * store = nil;
  2700. X        return trail;
  2701. X}
  2702. X
  2703. X
  2704. Xchar * lookupsym(id)
  2705. Xchar * id;
  2706. X/* Function: looks up id in the list of symbolic names starting
  2707. X * with pointer SYMBOLS, and returns a pointer to the corresponding
  2708. X * revision number. Returns nil if not present.
  2709. X */
  2710. X{
  2711. X        register struct assoc * next;
  2712. X        next = Symbols;
  2713. X        while (next!=nil) {
  2714. X                if (strcmp(id, next->symbol)==0)
  2715. X                        return(next->delta->num);
  2716. X                else    next=next->nextassoc;
  2717. X        }
  2718. X        return nil;
  2719. X}
  2720. X
  2721. Xint expandsym(source, target)
  2722. Xchar * source, * target;
  2723. X/* Function: Source points to a revision number. Expandsym copies
  2724. X * the number to target, but replaces all symbolic fields in the
  2725. X * source number with their numeric values.
  2726. X * A trailing '.' is omitted; leading zeroes are compressed.
  2727. X * returns false on error;
  2728. X */
  2729. X{       register char * sp, * tp, *bp;
  2730. X        char symbuf[30];
  2731. X        register enum tokens d;
  2732. X
  2733. X        sp = source; tp=target;
  2734. X        if (sp == nil) { /*accept nil pointer as a legal value*/
  2735. X                *tp='\0';
  2736. X                return true;
  2737. X        }
  2738. X
  2739. X        while (*sp != '\0') {
  2740. X                if (ctab[*sp] == DIGIT) {
  2741. X                        if (*sp=='0') {
  2742. X                                /* skip leading zeroes */
  2743. X                                sp++;
  2744. X                                while(*sp == '0') sp++;
  2745. X                                if (*sp=='\0' || *sp=='.') *tp++ = '0'; /*single zero*/
  2746. X                        }
  2747. X                        while(ctab[*sp] == DIGIT) *tp++ = *sp++;
  2748. X                        if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) {
  2749. X                                *tp='\0'; return true;
  2750. X                        }
  2751. X                        if (*sp == '.') *tp++ = *sp++;
  2752. X                        else {
  2753. X                            error("Improper revision number: %s",source);
  2754. X                            *tp = '\0';
  2755. X                            return false;
  2756. X                        }
  2757. X                } elsif (ctab[*sp] == LETTER) {
  2758. X                        bp = symbuf;
  2759. X                        do {    *bp++ = *sp++;
  2760. X                        } while(((d=ctab[*sp])==LETTER) || (d==DIGIT) ||
  2761. X                              (d==IDCHAR));
  2762. X                        *bp= '\0';
  2763. X                        bp=lookupsym(symbuf);
  2764. X                        if (bp==nil) {
  2765. X                                error("Symbolic number %s is undefined.",symbuf);
  2766. X                                *tp='\0';
  2767. X                                return false;
  2768. X                        } else { /* copy number */
  2769. X                                while (*tp++ = *bp++); /* copies the trailing \0*/
  2770. X                        }
  2771. X                        if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0')))
  2772. X                                return true;
  2773. X                        if (*sp == '.')  {
  2774. X                                *(tp-1) = *sp++;
  2775. X                        } else {
  2776. X                                error("Improper revision number: %s",source);
  2777. X                                return false;
  2778. X                        }
  2779. X                }else {
  2780. X                        error("Improper revision number: %s", source);
  2781. X                        *tp = '\0';
  2782. X                        return false;
  2783. X                }
  2784. X        }
  2785. X        *tp = '\0';
  2786. X        return true;
  2787. X}
  2788. X
  2789. X
  2790. X
  2791. X#ifdef REVTEST
  2792. X
  2793. Xmain(argc,argv)
  2794. Xint argc; char * argv[];
  2795. X{
  2796. X        char symrevno[revlength];       /* used for input of revision numbers */
  2797. X        char numricrevno[revlength];
  2798. X        char author[20];
  2799. X        char state[20];
  2800. X        char date[20];
  2801. X        struct hshentry * gendeltas[hshsize/2];
  2802. X        struct hshentry * target;
  2803. X        int i;
  2804. X
  2805. X        cmdid = "revtest";
  2806. X        if (argc<2) {
  2807. X                VOID fputs("No input file\n",stderr);
  2808. X                exit(-1);
  2809. X        }
  2810. X        if ((finptr=fopen(argv[1], "r")) == NULL) {
  2811. X                faterror("Can't open input file %s\n",argv[1]);
  2812. X        }
  2813. X        Lexinit();
  2814. X        getadmin();
  2815. X
  2816. X        gettree();
  2817. X
  2818. X        getdesc(false);
  2819. X
  2820. X        do {
  2821. X                /* all output goes to stderr, to have diagnostics and       */
  2822. X                /* errors in sequence.                                      */
  2823. X                VOID fprintf(stderr,"\nEnter revision number or <return> or '.': ");
  2824. X                if(gets(symrevno)==NULL) break;
  2825. X                if (*symrevno == '.') break;
  2826. X                VOID fprintf(stderr,"%s;\n",symrevno);
  2827. X                expandsym(symrevno,numricrevno);
  2828. X                VOID fprintf(stderr,"expanded number: %s; ",numricrevno);
  2829. X                VOID fprintf(stderr,"Date: ");
  2830. X                gets(date); VOID fprintf(stderr,"%s; ",date);
  2831. X                VOID fprintf(stderr,"Author: ");
  2832. X                gets(author);VOID fprintf(stderr,"%s; ",author);
  2833. X                VOID fprintf(stderr,"State: ");
  2834. X                gets(state); VOID fprintf(stderr, "%s;\n", state);
  2835. X                target=genrevs(numricrevno,*date=='\0'?(char *)nil:date, *author=='\0'?(char *)nil:author,
  2836. X                              *state=='\0'?(char *)nil:state,gendeltas);
  2837. X                if (target!=nil) {
  2838. X                        i=0;
  2839. X                        while (gendeltas[i]!=nil) {
  2840. X                                VOID fprintf(stderr,"%s\n",gendeltas[i++]->num);
  2841. X                        }
  2842. X                }
  2843. X        } while (true);
  2844. X        VOID fprintf(stderr,"done\n");
  2845. X
  2846. X}
  2847. X
  2848. Xcleanup(){}
  2849. X/*dummy*/
  2850. X
  2851. X#endif REVTEST
  2852. X@
  2853. X
  2854. X
  2855. X4.5.2.1
  2856. Xlog
  2857. X@Start of Amiga RCS port branch.
  2858. X@
  2859. Xtext
  2860. X@d5 1
  2861. Xa5 5
  2862. X<<<<<<< rcsrev.c
  2863. Xstatic char rcsid[]= "$Id: rcsrev.c,v 4.5.1.2 89/08/11 03:07:56 rsbx Exp Locker: rsbx $ Purdue CS";
  2864. X=======
  2865. Xstatic char rcsid[]= "$Id: rcsrev.c,v 1.2 89/09/17 13:36:25 rick Exp $ Purdue CS";
  2866. X>>>>>>> 1.2
  2867. Xa36 17
  2868. X<<<<<<< rcsrev.c
  2869. X * Revision 4.5.1.2  89/08/11  03:07:56  rsbx
  2870. X * Added eggert's 88/08/28 15:03:00 change to remove possibility of an internal
  2871. X * error and (same change) removed lint.
  2872. X * 
  2873. X * Revision 4.5.1.1  89/08/11  01:42:59  rsbx
  2874. X * Start of cbmvax RCS source branch.
  2875. X=======
  2876. X * Revision 1.2  89/09/17  13:36:25  rick
  2877. X * Port to AmigaDos done by Rick Schaeffer (ricks@@iscuva.iscs.com)
  2878. X * All changes done with conditional compile (#ifdef AMIGA).  This version
  2879. X * compiles correctly with Lattice C version 5.02 or later.
  2880. X * 
  2881. X * Revision 1.2  88/09/03  15:12:14  rick
  2882. X * Port to AmigaDos.  All done with conditional compiles
  2883. X>>>>>>> 1.2
  2884. X * 
  2885. Xa37 3
  2886. X * checked in with -k by rsbx at 89.08.10.16.22.50.
  2887. X * 
  2888. X * Revision 4.5  89/05/01  15:13:22  narten
  2889. Xd88 4
  2890. Xd93 1
  2891. Xd108 1
  2892. Xa108 1
  2893. X        do {
  2894. Xd110 1
  2895. Xa110 1
  2896. X        } while (*sp);
  2897. Xd239 1
  2898. Xa239 1
  2899. X        for (;;) {
  2900. Xd253 2
  2901. Xd256 7
  2902. Xa262 5
  2903. X        if ( n1 != n2 ) return n1-n2;
  2904. X        if ( --length == 0 ) return 0;
  2905. X        if ( *s1 == '\0' ) return 1;
  2906. X        if ( *s2 == '\0' ) return -1;
  2907. X    }
  2908. X@
  2909. X
  2910. X
  2911. X4.5.2.2
  2912. Xlog
  2913. X@Finished the integration of Rick Schaeffer's RCS Amiga port with the RCS
  2914. Xsources I have here (and are later than the ones Rick used).
  2915. X@
  2916. Xtext
  2917. X@d5 5
  2918. Xa9 1
  2919. Xstatic char rcsid[]= "$Id: rcsrev.c,v 4.5.2.1 89/10/13 19:19:08 rsbx Exp Locker: rsbx $ Purdue CS";
  2920. Xd41 1
  2921. Xa41 3
  2922. X * Revision 4.5.2.1  89/10/13  19:19:08  rsbx
  2923. X * Start of Amiga RCS port branch.
  2924. X * 
  2925. Xd48 9
  2926. X@
  2927. X
  2928. X
  2929. X4.5.1.1
  2930. Xlog
  2931. X@Start of cbmvax RCS source branch.
  2932. X@
  2933. Xtext
  2934. X@a37 3
  2935. X * checked in with -k by rsbx at 89.08.10.16.22.50.
  2936. X * 
  2937. X * Revision 4.5  89/05/01  15:13:22  narten
  2938. X@
  2939. X
  2940. X
  2941. X4.5.1.2
  2942. Xlog
  2943. X@Added eggert's 88/08/28 15:03:00 change to remove possibility of an internal
  2944. Xerror and (same change) removed lint.
  2945. X@
  2946. Xtext
  2947. X@d5 1
  2948. Xa5 1
  2949. Xstatic char rcsid[]= "$Id: rcsrev.c,v 4.5.1.1 89/08/11 01:42:59 rsbx Exp Locker: rsbx $ Purdue CS";
  2950. Xa36 3
  2951. X * Revision 4.5.1.1  89/08/11  01:42:59  rsbx
  2952. X * Start of cbmvax RCS source branch.
  2953. X * 
  2954. Xd91 4
  2955. Xd96 1
  2956. Xd111 1
  2957. Xa111 1
  2958. X        do {
  2959. Xd113 1
  2960. Xa113 1
  2961. X        } while (*sp);
  2962. Xd242 1
  2963. Xa242 1
  2964. X        for (;;) {
  2965. Xd256 2
  2966. Xd259 7
  2967. Xa265 5
  2968. X        if ( n1 != n2 ) return n1-n2;
  2969. X        if ( --length == 0 ) return 0;
  2970. X        if ( *s1 == '\0' ) return 1;
  2971. X        if ( *s2 == '\0' ) return -1;
  2972. X    }
  2973. X@
  2974. SHAR_EOF
  2975. echo "End of archive 10 (of 14)"
  2976. # if you want to concatenate archives, remove anything after this line
  2977. exit
  2978.